Commit 1e0d9a57a82715de413e761eef7acfe380dce483
Exists in
master
and in
23 other branches
Merge branch 'master' into rails-2.3.5
Conflicts: app/controllers/application.rb lib/noosfero.rb lib/noosfero/i18n.rb test/test_helper.rb test/unit/consumption_test.rb
Showing
1595 changed files
with
105574 additions
and
92651 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 1595 files displayed.
.gitignore.example
| ... | ... | @@ -1,28 +0,0 @@ |
| 1 | -.gitignore | |
| 2 | -vendor/rails | |
| 3 | -doc/api | |
| 4 | -doc/plugins | |
| 5 | -tmp | |
| 6 | -config/database.yml | |
| 7 | -config/session.secret | |
| 8 | -config/mail.yml | |
| 9 | -config/ferret_server.yml | |
| 10 | -config/mongrel_cluster.yml | |
| 11 | -index | |
| 12 | -locale | |
| 13 | -log | |
| 14 | -public/articles | |
| 15 | -public/images/0000 | |
| 16 | -public/thumbnails | |
| 17 | -public/user_themes | |
| 18 | -public/designs/themes/default | |
| 19 | -public/javascripts/cache*.js | |
| 20 | -public/stylesheets/cache*.css | |
| 21 | -db/development.db | |
| 22 | -db/production.db | |
| 23 | -db/test.db | |
| 24 | -doc/noosfero/*.xhtml | |
| 25 | -doc/noosfero/*/*.xhtml | |
| 26 | -tags | |
| 27 | -pkg | |
| 28 | -*~ |
.mailmap
| 1 | 1 | Antonio Terceiro <terceiro@colivre.coop.br> <AntonioTerceiro@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
| 2 | 2 | Antonio Terceiro <terceiro@colivre.coop.br> <terceiro@softwarelivre.org> |
| 3 | 3 | Aurelio A. Heckert <aurelio@colivre.coop.br> <AurelioAHeckert@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
| 4 | +Aurelio A. Heckert <aurelio@colivre.coop.br> <aurelio@colivre.coop.br> | |
| 5 | +Aurelio A. Heckert <aurelio@colivre.coop.br> <aurium@gmail.com> | |
| 6 | +Caio SBA <caiosba@gmail.com> <caiosba@safernet.org.br> | |
| 4 | 7 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <DanielaFeitosa@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
| 5 | 8 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <danielafeitosa@colivre.coop.br> |
| 6 | 9 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <daniela@sede.colivre.coop.br> |
| 7 | 10 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> <dani@lindinha.(none)> |
| 11 | +Daniel Cunha <daniel@colivre.coop.br> <daniel.ccunha@gmail.com> | |
| 12 | +Grazieno Pellegrino <grazieno@gmail.com> <GrazienoPellegrino@3f533792-8f58-4932-b0fe-aaf55b0a4547> | |
| 8 | 13 | Joenio Costa <joenio@colivre.coop.br> <JoenioCosta@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
| 9 | 14 | Joenio Costa <joenio@colivre.coop.br> <joenio@perl.org.br> |
| 10 | -Grazieno Pellegrino <grazieno@gmail.com> <GrazienoPellegrino@3f533792-8f58-4932-b0fe-aaf55b0a4547> | |
| 11 | 15 | Leandro Nunes dos Santos <leandronunes@gmail.com> <LeandroNunes@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
| 12 | 16 | Moises Machado <moises@colivre.coop.br> <MoisesMachado@3f533792-8f58-4932-b0fe-aaf55b0a4547> |
| 13 | 17 | Valessio Brito <valessio@gmail.com> <ValessioBrito@3f533792-8f58-4932-b0fe-aaf55b0a4547> | ... | ... |
AUTHORS
| ... | ... | @@ -8,13 +8,18 @@ Developers |
| 8 | 8 | |
| 9 | 9 | Antonio Terceiro <terceiro@colivre.coop.br> |
| 10 | 10 | Aurelio A. Heckert <aurelio@colivre.coop.br> |
| 11 | +Bráulio Bhavamitra <brauliobo@gmail.com> | |
| 12 | +Caio SBA <caiosba@gmail.com> | |
| 11 | 13 | Daniela Soares Feitosa <danielafeitosa@colivre.coop.br> |
| 14 | +Daniel Cunha <daniel@colivre.coop.br> | |
| 12 | 15 | Fernanda Lopes <nanda.listas+psl@gmail.com> |
| 13 | 16 | Grazieno Pellegrino <grazieno@gmail.com> |
| 14 | 17 | Italo Valcy <italo@dcc.ufba.br> |
| 15 | 18 | Joenio Costa <joenio@colivre.coop.br> |
| 16 | 19 | Josef Spillner <josef.spillner@tu-dresden.de> |
| 20 | +Keilla Menezes <keilla@colivre.coop.br> | |
| 17 | 21 | Leandro Nunes dos Santos <leandronunes@gmail.com> |
| 22 | +LinguÁgil 2010 <linguagil.bahia@gmail.com> | |
| 18 | 23 | Martín Olivera <molivera@solar.org.ar> |
| 19 | 24 | Moises Machado <moises@colivre.coop.br> |
| 20 | 25 | Nanda Lopes <nanda.listas+psl@gmail.com> |
| ... | ... | @@ -22,6 +27,7 @@ Raphaël Rousseau <raph@r4f.org> |
| 22 | 27 | Raquel Lira <raquel.lira@gmail.com> |
| 23 | 28 | Rodrigo Souto <rodrigo@colivre.coop.br> |
| 24 | 29 | Ronny Kursawe <kursawe.ronny@googlemail.com> |
| 30 | +Samuel R. C. Vale <srcvale@holoscopio.com> | |
| 25 | 31 | Valessio Brito <valessio@gmail.com> |
| 26 | 32 | Yann Lugrin <yann.lugrin@liquid-concept.ch> |
| 27 | 33 | ... | ... |
HACKING
| ... | ... | @@ -61,3 +61,26 @@ The above command makes the server available at http://localhost:9999/ |
| 61 | 61 | |
| 62 | 62 | The sample-data data scripts creates one administrator user with login "ze" and |
| 63 | 63 | password "test". |
| 64 | + | |
| 65 | +Note that some operations, like generating image thumbnails, sending e-mails, | |
| 66 | +etc, are done in background in the context of a service independent from the | |
| 67 | +Rails application server. To have those tasks performed in a development | |
| 68 | +environment, you must run the delayed_job server like this: | |
| 69 | + | |
| 70 | + ./script/delayed_job run | |
| 71 | + | |
| 72 | +This will block your terminal. To stop the delayed_job server, hit Control-C. | |
| 73 | + | |
| 74 | +== Enabling exceptions notification | |
| 75 | + | |
| 76 | +By default, exception notifications are disabled in development environment. If | |
| 77 | +you want to enable it then you need to change some files: | |
| 78 | + | |
| 79 | +1) Add in config/environments/development.rb: | |
| 80 | + config.action_controller.consider_all_requests_local = false | |
| 81 | + | |
| 82 | +2) Add in app/controller/application.rb: | |
| 83 | + local_addresses.clear | |
| 84 | + | |
| 85 | +3) Add in config/noosfero.yml at development section: | |
| 86 | + exception_recipients: [admin@example.com] | ... | ... |
INSTALL
| ... | ... | @@ -13,13 +13,14 @@ You need to install some packages Noosfero depends on. On Debian GNU/Linux or |
| 13 | 13 | Debian-based systems, all of these packages are available through the Debian |
| 14 | 14 | archive. You can install them with the following command: |
| 15 | 15 | |
| 16 | - # aptitude install ruby rake libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby libferret-ruby libdaemons-ruby mongrel mongrel-cluster tango-icon-theme | |
| 16 | + # apt-get install ruby rake po4a libgettext-ruby-util libgettext-ruby-data libgettext-ruby1.8 libsqlite3-ruby rcov librmagick-ruby libredcloth-ruby libwill-paginate-ruby iso-codes libfeedparser-ruby libferret-ruby libdaemons-ruby mongrel mongrel-cluster tango-icon-theme libhpricot-ruby | |
| 17 | 17 | |
| 18 | 18 | On other systems, they may or may not be available through your regular package |
| 19 | 19 | management system. Below are the links to their homepages. |
| 20 | 20 | |
| 21 | 21 | * Ruby: http://www.ruby-lang.org/ |
| 22 | 22 | * Rake: http://rake.rubyforge.org/ |
| 23 | +* po4a: http://po4a.alioth.debian.org/ | |
| 23 | 24 | * Ruby-GetText: http://www.yotabanana.com/hiki/ruby-gettext.html?ruby-gettext (at least version 1.9.0) |
| 24 | 25 | * Ruby-sqlite3: http://rubyforge.org/projects/sqlite-ruby |
| 25 | 26 | * rcov: http://eigenclass.org/hiki/rcov |
| ... | ... | @@ -32,79 +33,268 @@ management system. Below are the links to their homepages. |
| 32 | 33 | * Daemons - http://daemons.rubyforge.org/ |
| 33 | 34 | * Mongrel: http://mongrel.rubyforge.org/ |
| 34 | 35 | * tango-icon-theme: http://tango.freedesktop.org/Tango_Icon_Library |
| 36 | +* Hpricot: http://hpricot.com/ | |
| 35 | 37 | |
| 36 | -If you manage to install Noosfero successfully, please feel free to contact the | |
| 37 | -Noosfero development mailing with the instructions for doing so, and we'll | |
| 38 | -include it here. | |
| 38 | +Note: the tango-icon-theme package is not available in Debian Lenny's main | |
| 39 | +repository, because back then it was not DFSG-free (Debian Squeeze will have it | |
| 40 | +in main). You can either add the non-free repository to your sources.list file, | |
| 41 | +or download the .deb directly and install it manually with `dpkg -i`. In this | |
| 42 | +case will need do install hicolor-icon-theme as well bacause tango-icon-theme | |
| 43 | +depends on it. After that you can try the command line above again, but | |
| 44 | +without "tango-icon-theme". | |
| 45 | + | |
| 46 | +If you manage to install Noosfero successfully on other systems than Debian, | |
| 47 | +please feel free to contact the Noosfero development mailing with the | |
| 48 | +instructions for doing so, and we'll include it here. | |
| 39 | 49 | |
| 40 | 50 | === Setting up a production environment |
| 41 | 51 | |
| 52 | +DISCLAIMER: this installation procedure is tested with Debian stable, which is | |
| 53 | +currently the only recommended operating system for production usage. It is | |
| 54 | +possible that you can install it on other systems, and if you do so, please | |
| 55 | +report it on one of the Noosfero mailing lists, and please send a patch | |
| 56 | +updating these instructions. | |
| 57 | + | |
| 58 | +As root user | |
| 59 | +============ | |
| 60 | + | |
| 42 | 61 | NOTE: these instructions are for seting up a *production* environment. If you |
| 43 | -are going to do Noosfero development, you don't need to do these steps. See the | |
| 44 | -HACKING file instead. | |
| 62 | +are going to do Noosfero development, you don't need to do these steps. Stop | |
| 63 | +here and see the HACKING file instead. | |
| 64 | + | |
| 65 | +Install memcached. On Debian: | |
| 66 | + | |
| 67 | +# apt-get install memcached | |
| 68 | + | |
| 69 | +Study whether you need to raise the ammount of memory it uses for caching, | |
| 70 | +depending on the demand you expect for your site. If you are going to run a | |
| 71 | +high-traffic site, you will want to raise the ammount of memory reserved for | |
| 72 | +caching. | |
| 73 | + | |
| 74 | +It is recommended that you run noosfero with its own user account. To create | |
| 75 | +such an account, please do the following: | |
| 76 | + | |
| 77 | +# adduser --system --group noosfero --shell /bin/sh --home /var/lib/noosfero | |
| 78 | + | |
| 79 | +(note that you can change the $HOME directory of the user if you wish, here we | |
| 80 | +are using /var/lib/noosfero) | |
| 81 | + | |
| 82 | +The --system option will tell adduser to create a system user, i.e. this user | |
| 83 | +will not have a password and cannot login to the system directly. To become | |
| 84 | +this user, you have to use sudo: | |
| 85 | + | |
| 86 | +# sudo -u noosfero -i | |
| 87 | + | |
| 88 | +or | |
| 89 | + | |
| 90 | +# su - noosfero | |
| 91 | + | |
| 92 | +As noosfero user | |
| 93 | +================ | |
| 94 | + | |
| 95 | +downloading from git | |
| 96 | +-------------------- | |
| 97 | + | |
| 98 | +Here we are cloning the noosfero repository from git. Note: you will need to | |
| 99 | +install git before. | |
| 100 | + | |
| 101 | +$ git clone git://git.colivre.coop.br/noosfero.git current | |
| 102 | +$ cd current | |
| 103 | +$ git checkout -b stable origin/stable | |
| 104 | + | |
| 105 | +downloading tarball | |
| 106 | +------------------- | |
| 107 | + | |
| 108 | +Note: replace 0.27.1 below from the latest stable version. | |
| 109 | + | |
| 110 | +$ wget http://noosfero.org/pub/Development/NoosferoVersion00x27x01/noosfero-0.27.1.tar.gz | |
| 111 | +$ tar -zxvf noosfero-0.27.1.tar.gz | |
| 112 | +$ ln -s noosfero-0.27.1 current | |
| 113 | +$ cd current | |
| 114 | + | |
| 115 | +Copy config/ferret_server.yml.dist to config/ferret_server.yml. You will | |
| 116 | +probably not need to customize this configuration, but have a look at it. | |
| 117 | + | |
| 118 | +Create the mongrel configuration file: | |
| 119 | + | |
| 120 | +$ mongrel_rails cluster::configure | |
| 121 | + | |
| 122 | +Edit config/mongrel_cluster.yml to suit your needs. Make sure your apache | |
| 123 | +configuration matches the mongrel cluster configuration, specially in respect | |
| 124 | +to the ports and numbers of mongrel instances. | |
| 125 | + | |
| 126 | +Note: currently Noosfero only supports Rails 2.1.0, which is the version in | |
| 127 | +Debian Lenny. If you have a Rails version newer than that, Noosfero will | |
| 128 | +probably not work. You can install Rails 2.1.0 into your Noosfero installation | |
| 129 | +with the following procedure: | |
| 130 | + | |
| 131 | +$ cd /var/lib/noosfero/current/vendor | |
| 132 | +$ wget http://ftp.de.debian.org/debian/pool/main/r/rails/rails_2.1.0.orig.tar.gz | |
| 133 | +$ tar xzf rails_2.1.0.orig.tar.gz | |
| 134 | +$ ln -s rails-2.1.0 rails | |
| 135 | + | |
| 136 | +As root user | |
| 137 | +============ | |
| 138 | + | |
| 139 | +Setup Noosfero log and tmp directories: | |
| 140 | + | |
| 141 | +# cd /var/lib/noosfero/current | |
| 142 | +# ./etc/init.d/noosfero setup | |
| 143 | + | |
| 144 | +Now it's time to setup the database. In this example we are using PostgreSQL, | |
| 145 | +so if you are planning to use a different database this steps won't apply. | |
| 146 | + | |
| 147 | +# apt-get install postgresql libpgsql-ruby | |
| 148 | +# su postgres -c 'createuser noosfero -S -d -R' | |
| 149 | + | |
| 150 | +By default Rails will try to connect on postgresql through 5432 port, but | |
| 151 | +Debian start postgresql on port 5433, then is needed to change postgresql to | |
| 152 | +start on port 5432 in /etc/postgresql/8.3/main/postgresql.conf file. | |
| 153 | + | |
| 154 | +Restart postgresql: | |
| 155 | + | |
| 156 | +# invoke-rc.d postgresql-8.3 restart | |
| 157 | + | |
| 158 | +Noosfero needs a functional e-mail setup to work: the local mail system should | |
| 159 | +be able to deliver e-mail to the internet, either directly or through an | |
| 160 | +external SMTP server. | |
| 161 | + | |
| 162 | +If you know mail systems well, you just need to make sure thet the local MTA, | |
| 163 | +listening on localhost:25, is able to deliver e-mails to the internet. Any mail | |
| 164 | +server will do it. | |
| 165 | + | |
| 166 | +If you are not a mail specialist, we suggest that you use the Postfix mail | |
| 167 | +server, since it is easy to configure and very reliable. Just follow the | |
| 168 | +instructions below. | |
| 169 | + | |
| 170 | +To install Postfix: | |
| 171 | + | |
| 172 | +# apt-get install postfix | |
| 173 | + | |
| 174 | +During the installation process, you will be asked a few questions. Your answer | |
| 175 | +to them will vary in 2 cases: | |
| 176 | + | |
| 177 | +Case 1: you can send e-mails directly to the internet. This will be the case | |
| 178 | +for most commercial private servers. Your answers should be: | |
| 179 | + | |
| 180 | + General type of mail configuration: Internet site | |
| 181 | + System mail name: the name of your domain, e.g. "mysocialnetwork.com" | |
| 182 | + | |
| 183 | +Case 2: you cannot, or don't want to, send e-mail directly to the internet. | |
| 184 | +This happens for example if your server is not allowed to make outbound | |
| 185 | +connections on port 25, or if you want to concentrate all your outbound mail | |
| 186 | +through a single SMTP server. Your answers in this case should be: | |
| 187 | + | |
| 188 | + General type of mail configuration: Internet with smarthost | |
| 189 | + System mail name: the name of your domain, e.g. "mysocialnetwork.com" | |
| 190 | + SMTP relay host: smtp.yourprovider.com | |
| 191 | + | |
| 192 | +Note that smtp.yourprovider.com must allow your server to deliver e-mails | |
| 193 | +through it. You should probably ask your servive provider about this. | |
| 194 | + | |
| 195 | +There is another possibility: if you are installing on a shared server, and | |
| 196 | +don't have permission to configure the local MTA, you can instruct Noosfero to | |
| 197 | +send e-mails directly through an external server. Please note that this should | |
| 198 | +be your last option, since contacting an external SMTP server directly may slow | |
| 199 | +down your Noosfero application server. To configure Noosfero to send e-mails | |
| 200 | +through an external SMTP server, follow the instructions on | |
| 201 | +http://noosfero.org/Development/SMTPMailSending | |
| 45 | 202 | |
| 46 | -* install memcached. Study whether you need to raise the ammount of memory it uses for caching, depending on the demand you expect for your site. | |
| 47 | -* enter the directory where you unpacked noosfero | |
| 48 | -* copy config/ferret_server.yml.dist to config/ferret_server.yml | |
| 49 | -* create the mongrel configuration file: `mongrel_rails cluster::configure` | |
| 50 | -** then edit config/mongrel_cluster.yml to suit your environment. Make sure your apache configuration matches the mongrel cluster configuration, specially in respect to the ports and numbers of mongrel instances. | |
| 51 | -* create needed temporary directories: | |
| 52 | - mkdir tmp | |
| 53 | - mkdir tmp/pids | |
| 54 | - mkdir log | |
| 55 | -* create database (example using PostgreSQL, YMMV) | |
| 203 | +As noosfero user | |
| 204 | +================ | |
| 56 | 205 | |
| 57 | - root user | |
| 58 | - ========= | |
| 59 | - # sudo apt-get install postgresql libpgsql-ruby | |
| 60 | - # su - postgres | |
| 206 | +Now create the databases: | |
| 61 | 207 | |
| 62 | - postgres user | |
| 63 | - ============= | |
| 64 | - postgres@HOST:~$ createuser noosfero | |
| 65 | - Shall the new role be a superuser? (y/n) n | |
| 66 | - Shall the new role be allowed to create databases? (y/n) y | |
| 67 | - Shall the new role be allowed to create more new roles? (y/n) n | |
| 208 | +$ cd /var/lib/noosfero/current | |
| 209 | +$ createdb noosfero_production | |
| 210 | +$ createdb noosfero_development | |
| 211 | +$ createdb noosfero_test | |
| 68 | 212 | |
| 69 | - noosfero_user | |
| 70 | - ============= | |
| 71 | - createdb noosfero_production | |
| 72 | - createdb noosfero_development | |
| 73 | - createdb noosfero_test | |
| 213 | +The development and test databases are actually optional. If you are creating a | |
| 214 | +stricly production server, you will probably not need them. | |
| 74 | 215 | |
| 75 | -* configure database access in config/database.yml | |
| 216 | +Now we want to configure Noosfero for accessing the database we just created. | |
| 217 | +To do that, you can 1) copy config/database.yml.pgsql to config/database.yml, | |
| 218 | +or create config/database.yml from scratch with the following content: | |
| 76 | 219 | |
| 77 | -* test database access: | |
| 78 | -** first create the development database | |
| 79 | - rake db:schema:load | |
| 80 | -** if everything goes right, then create the production database: | |
| 81 | - RAILS_ENV=production rake db:schema:load | |
| 220 | + production: | |
| 221 | + adapter: postgresql | |
| 222 | + encoding: unicode | |
| 223 | + database: noosfero_production | |
| 224 | + username: noosfero | |
| 82 | 225 | |
| 83 | -* create sample data: | |
| 84 | - RAILS_ENV=production rake db:populate | |
| 226 | +Now, to test the database access, you can fire the Rails database console: | |
| 85 | 227 | |
| 86 | -* Add the domain name you will be using for your noosfero site to the list of | |
| 87 | - environment domain, like this: | |
| 228 | +$ ./script/dbconsole production | |
| 88 | 229 | |
| 89 | - ./script/runner "Environment.default.domains << Domain.new(:name => 'your.domain.com')" | |
| 230 | +If it connects to your database, then everything is fine. If you got an error | |
| 231 | +message, then you have to check your database configuration. | |
| 90 | 232 | |
| 91 | - (of course, replace your.domain.com with your real domain) | |
| 233 | +Create the database structure: | |
| 92 | 234 | |
| 93 | -* compile the translations: | |
| 94 | - rake makemo | |
| 235 | +$ RAILS_ENV=production rake db:schema:load | |
| 95 | 236 | |
| 96 | -* start the server: | |
| 97 | - ./script/production start | |
| 237 | +Now we have to create some initial data. To create your default environment | |
| 238 | +(the first one), run the command below: | |
| 98 | 239 | |
| 99 | -* to stop the server: | |
| 240 | +$ RAILS_ENV=production ./script/runner 'Environment.create!(:name => "My environment", :is_default => true)' | |
| 100 | 241 | |
| 101 | - ./script/production stop | |
| 242 | +(of course, replace "My environment" with your environment's name!) | |
| 102 | 243 | |
| 103 | -* to restart the server: | |
| 244 | +And now you have to add the domain name you will be using for your noosfero | |
| 245 | +site to the list of domains of that default environment you just created: | |
| 104 | 246 | |
| 105 | - ./script/production restart | |
| 247 | +$ RAILS_ENV=production ./script/runner "Environment.default.domains << Domain.new(:name => 'your.domain.com')" | |
| 106 | 248 | |
| 107 | -* enable the following apache modules: | |
| 249 | +(replace "your.domain.com" with your actual domain name) | |
| 250 | + | |
| 251 | +Add at least one user as admin of environment: | |
| 252 | + | |
| 253 | +$ RAILS_ENV=production ./script/runner "User.create(:login => 'adminuser', :email => 'admin@example.com', :password => 'admin', :password_confirmation => 'admin', :environment => Environment.default)" | |
| 254 | + | |
| 255 | +(replace "adminuser", "admin@example.com", "admin" with the login, email | |
| 256 | +and password of your environment admin) | |
| 257 | + | |
| 258 | +Compile the translations: | |
| 259 | + | |
| 260 | +$ RAILS_ENV=production rake noosfero:translations:compile | |
| 261 | + | |
| 262 | +To start the Noosfero application servers: | |
| 263 | + | |
| 264 | +$ ./script/production start | |
| 265 | + | |
| 266 | +At this point you have a functional Noosfero installation running, the only | |
| 267 | +thing left is to configure your webserver as a reverse proxy to pass requests | |
| 268 | +to them. | |
| 269 | + | |
| 270 | +Enabling exception notifications | |
| 271 | +================================ | |
| 272 | + | |
| 273 | +This is an optional step. You will need it only if you want to receive e-mail | |
| 274 | +notifications when some exception occurs on Noosfero. | |
| 275 | + | |
| 276 | +First, install this version of the gem. | |
| 277 | +Others versions may not be compatible with Noosfero: | |
| 278 | + | |
| 279 | +# gem install exception_notification -v 1.0.20090728 | |
| 280 | + | |
| 281 | +You can configure the e-mails that will receive the notifications. | |
| 282 | +Change the file config/noosfero.yml as the following example, replacing the | |
| 283 | +e-mails by real ones: | |
| 284 | + | |
| 285 | + production: | |
| 286 | + exception_recipients: [admin@example.com, you@example.com] | |
| 287 | + | |
| 288 | +================== | |
| 289 | +Apache instalation | |
| 290 | +================== | |
| 291 | + | |
| 292 | +# apt-get install apache2 | |
| 293 | + | |
| 294 | +Apache configuration | |
| 295 | +-------------------- | |
| 296 | + | |
| 297 | +First you have to enable the following some apache modules: | |
| 108 | 298 | |
| 109 | 299 | deflate |
| 110 | 300 | expires |
| ... | ... | @@ -113,26 +303,32 @@ HACKING file instead. |
| 113 | 303 | proxy_http |
| 114 | 304 | rewrite |
| 115 | 305 | |
| 116 | - On Debian GNU/Linux system, these modules can be enabled with the following | |
| 117 | - command line, as root: | |
| 306 | +On Debian GNU/Linux system, these modules can be enabled with the following | |
| 307 | +command line, as root: | |
| 118 | 308 | |
| 119 | - a2enmod deflate expires proxy proxy_balancer proxy_http rewrite | |
| 309 | +# a2enmod deflate expires proxy proxy_balancer proxy_http rewrite | |
| 120 | 310 | |
| 121 | - In other systems the way by which you enable apache modules may be different. | |
| 311 | +In other systems the way by which you enable apache modules may be different. | |
| 122 | 312 | |
| 123 | -* Configure apache web server as a reverse proxy. You can use the template | |
| 124 | - below, replacing /path/to/noosfero with the directory in which your noosfero | |
| 125 | - installation is, your.domain.com with the domain name of your noosfero site. | |
| 126 | - We are assuming that you are running two mongrel instances on ports 4000 and | |
| 127 | - 4001. If your setup is different you'll need to adjust <Proxy> section. If | |
| 128 | - you don't understand something in the configuration, please refer to the | |
| 129 | - apache documentation. | |
| 313 | +Now with the Apache configuration. You can use the template below, replacing | |
| 314 | +/var/lib/noosfero/current with the directory in which your noosfero | |
| 315 | +installation is, your.domain.com with the domain name of your noosfero site. | |
| 316 | +We are assuming that you are running two mongrel instances on ports 3000 and | |
| 317 | +3001. If your setup is different you'll need to adjust <Proxy> section. If you | |
| 318 | +don't understand something in the configuration, please refer to the apache | |
| 319 | +documentation. | |
| 320 | + | |
| 321 | +Add a file called "mysite" (or whatever name you want to give to your noosfero | |
| 322 | +site) to /etc/apache2/sites-available with the following content, and customize | |
| 323 | +as needed (as usual, make sure you replace "your.domain.com" with you actual | |
| 324 | +domain name, and "/var/lib/noosfero/current" with the directory where Noosfero | |
| 325 | +is installed): | |
| 130 | 326 | |
| 131 | 327 | <VirtualHost *:80> |
| 132 | 328 | ServerName your.domain.com |
| 133 | 329 | |
| 134 | - DocumentRoot "/path/to/noosfero/public" | |
| 135 | - <Directory "/path/to/noosfero/public"> | |
| 330 | + DocumentRoot "/var/lib/noosfero/current/public" | |
| 331 | + <Directory "/var/lib/noosfero/current/public"> | |
| 136 | 332 | Options FollowSymLinks |
| 137 | 333 | AllowOverride None |
| 138 | 334 | Order Allow,Deny |
| ... | ... | @@ -156,18 +352,110 @@ HACKING file instead. |
| 156 | 352 | LogLevel warn |
| 157 | 353 | CustomLog /var/log/apache2/noosfero.access.log combined |
| 158 | 354 | |
| 159 | - Include /path/to/noosfero/etc/noosfero/apache/cache.conf | |
| 355 | + Include /var/lib/noosfero/current/etc/noosfero/apache/cache.conf | |
| 160 | 356 | |
| 161 | 357 | </VirtualHost> |
| 162 | 358 | |
| 163 | 359 | <Proxy balancer://noosfero> |
| 164 | - BalancerMember http://127.0.0.1:4000 | |
| 165 | - BalancerMember http://127.0.0.1:4001 | |
| 360 | + BalancerMember http://127.0.0.1:3000 | |
| 361 | + BalancerMember http://127.0.0.1:3001 | |
| 166 | 362 | Order Allow,Deny |
| 167 | 363 | Allow from All |
| 168 | 364 | </Proxy> |
| 169 | 365 | |
| 170 | - The cache.conf file included in the end of the <VirtualHost> section is | |
| 171 | - important, since it will tell apache to pass expiration and cache headers to | |
| 172 | - clients so that the site feels faster for users. Do we need to say that using | |
| 173 | - that configuration is strongly recommended? | |
| 366 | +The cache.conf file included in the end of the <VirtualHost> section is | |
| 367 | +important, since it will tell apache to pass expiration and cache headers to | |
| 368 | +clients so that the site feels faster for users. Do we need to say that using | |
| 369 | +that configuration is strongly recommended? | |
| 370 | + | |
| 371 | +Enable that site with (as root, replace "mysite" with the actual name you gave | |
| 372 | +to your site configuration): | |
| 373 | + | |
| 374 | +# a2ensite mysite | |
| 375 | + | |
| 376 | +Now restart your apache server (as root): | |
| 377 | + | |
| 378 | +# invoke-rc.d apache2 restart | |
| 379 | + | |
| 380 | +============ | |
| 381 | +Maintainance | |
| 382 | +============ | |
| 383 | + | |
| 384 | +To ease the maintainance, install a symbolic link for the Noosfero startup | |
| 385 | +script in your server and add it to the system initialization and shutdown | |
| 386 | +sequences (as root): | |
| 387 | + | |
| 388 | +# ln -s /var/lib/noosfero/current/etc/init.d/noosfero /etc/init.d/noosfero | |
| 389 | +# update-rc.d noosfero defaults | |
| 390 | + Adding system startup for /etc/init.d/noosfero ... | |
| 391 | + /etc/rc0.d/K20noosfero -> ../init.d/noosfero | |
| 392 | + /etc/rc1.d/K20noosfero -> ../init.d/noosfero | |
| 393 | + /etc/rc6.d/K20noosfero -> ../init.d/noosfero | |
| 394 | + /etc/rc2.d/S20noosfero -> ../init.d/noosfero | |
| 395 | + /etc/rc3.d/S20noosfero -> ../init.d/noosfero | |
| 396 | + /etc/rc4.d/S20noosfero -> ../init.d/noosfero | |
| 397 | + /etc/rc5.d/S20noosfero -> ../init.d/noosfero | |
| 398 | + | |
| 399 | +Now to start Noosfero, you do as root: | |
| 400 | + | |
| 401 | +# invoke-rc.d noosfero start | |
| 402 | + | |
| 403 | +To stop Noosfero: | |
| 404 | + | |
| 405 | +# invoke-rc.d noosfero start | |
| 406 | + | |
| 407 | +To restart Noosfero: | |
| 408 | + | |
| 409 | +# invoke-rc.d noosfero restart | |
| 410 | + | |
| 411 | +Noosfero will be automatically started during system boot, and automatically | |
| 412 | +stopped if the system shuts down for some reason (or during the shutdown part | |
| 413 | +of a reboot). | |
| 414 | + | |
| 415 | +============= | |
| 416 | +Rotating logs | |
| 417 | +============= | |
| 418 | + | |
| 419 | +Noosfero provides an example logrotate configuation to rotate its logs. To use | |
| 420 | +it, create a symbolic link in /etc/logrotate.d/: | |
| 421 | + | |
| 422 | +# cd /etc/logrotate.d/ | |
| 423 | +# ln -s /var/lib/noosfero/current/etc/logrotate.d/noosfero | |
| 424 | + | |
| 425 | +Note that the provided file assumes Noosfero logging is being done in | |
| 426 | +/var/log/noosfero (which is the case if you followed the instructions above | |
| 427 | +correctly). If the logs are stored elsewhere, it's recommended that you copy | |
| 428 | +the file over to /etc/logrotate.d/ and modify it to point to your local log | |
| 429 | +directly. | |
| 430 | + | |
| 431 | +========= | |
| 432 | +Upgrading | |
| 433 | +========= | |
| 434 | + | |
| 435 | +If you followed the steps in this document and installed Noosfero from the git | |
| 436 | +repository, then upgrading is easy. First, you need to allow the noosfero user | |
| 437 | +to restart the memcached server with sudo, by adding the following line in | |
| 438 | +/etc/sudoers: | |
| 439 | + | |
| 440 | +noosfero ALL=NOPASSWD: /etc/init.d/memcached | |
| 441 | + | |
| 442 | +Then, to perform an upgrade, do the following as the noosfero user: | |
| 443 | + | |
| 444 | +$ cd /var/lib/noosfero/current | |
| 445 | +$ ./script/git-upgrade | |
| 446 | + | |
| 447 | +The git-upgrade script will take care of everything for you. It will first stop | |
| 448 | +the service, then fetch the current source code, upgrade database, compile | |
| 449 | +translations, and then start the service again. | |
| 450 | + | |
| 451 | +Note 1: make sure your local git repository is following the "stable" branch, | |
| 452 | +just like the instructions above. The "master" branch is not recommended for | |
| 453 | +use in production environments. | |
| 454 | + | |
| 455 | +Note 2: always read the release notes before upgrading. Sometimes there will be | |
| 456 | +steps that must be performed manually. If that is the case, you can invoke the | |
| 457 | +git-upgrade script with the special parameter "--shell" that will give you a | |
| 458 | +shell after the upgrade, which you can use to perform any manual steps | |
| 459 | +required: | |
| 460 | + | |
| 461 | +$ ./script/git-upgrade --shell | ... | ... |
| ... | ... | @@ -0,0 +1,255 @@ |
| 1 | +== XMPP/Chat Client Setup | |
| 2 | + | |
| 3 | +To configure XMPP/BOSH in Noosfero you need: | |
| 4 | + | |
| 5 | +* REST Client - http://github.com/archiloque/rest-client | |
| 6 | +* SystemTimer - http://ph7spot.com/musings/system-timer | |
| 7 | +* Pidgin data files - http://www.pidgin.im/ | |
| 8 | + | |
| 9 | +If you use Debian Lenny: | |
| 10 | + | |
| 11 | +# apt-get install librestclient-ruby (from backports) | |
| 12 | +# apt-get install pidgin-data | |
| 13 | +# apt-get install ruby1.8-dev | |
| 14 | +# gem install SystemTimer | |
| 15 | + | |
| 16 | +Take a look at util/chat directory to see samples of config file to configure a | |
| 17 | +XMPP/BOSH server with ejabberd, postgresql and apache2. | |
| 18 | + | |
| 19 | +== XMPP/Chat Server Setup | |
| 20 | + | |
| 21 | +This is a step-by-step guide to get a XMPP service working, in a Debian system. | |
| 22 | + | |
| 23 | +1. Install the required packages | |
| 24 | + | |
| 25 | +# apt-get -t lenny-backports install ejabberd | |
| 26 | +# apt-get install odbc-postgresql | |
| 27 | + | |
| 28 | +2. Ejabberd configuration | |
| 29 | + | |
| 30 | +All the following changes must be done in config file: | |
| 31 | + | |
| 32 | + /etc/ejabberd/ejabberd.cfg | |
| 33 | + | |
| 34 | + 2.1. Set the default admin user | |
| 35 | + | |
| 36 | +{ acl, admin, { user, "john", "www.example.com" } }. | |
| 37 | +{ acl, admin, { user, "bart", "www.example.com" } }. | |
| 38 | + | |
| 39 | + 2.2. Set the default host | |
| 40 | + | |
| 41 | +{ hosts, [ "www.example.com" ] }. | |
| 42 | + | |
| 43 | + 2.3. Http-Bind activation | |
| 44 | + | |
| 45 | +{ 5280, ejabberd_http, [ | |
| 46 | + http_bind, | |
| 47 | + web_admin | |
| 48 | + ] | |
| 49 | +} | |
| 50 | + | |
| 51 | +(...) | |
| 52 | + | |
| 53 | +{ modules, [ | |
| 54 | + {mod_http_bind, []}, | |
| 55 | + ... | |
| 56 | +] }. | |
| 57 | + | |
| 58 | +Ejabberd creates semi-anonymous rooms by default, but Noosfero's Jabber client | |
| 59 | +needs non-anonymous room, then we need to change default params of creation | |
| 60 | +rooms in ejabberd to create non-anonymous rooms. | |
| 61 | + | |
| 62 | +In non-anonymous rooms the jabber service sends the new occupant's full JID to | |
| 63 | +all occupants in the room[1]. | |
| 64 | + | |
| 65 | +Add option "{default_room_options, [{anonymous, false}]}" to | |
| 66 | +/etc/ejabberd/ejabberd.cfg in mod_muc session. See below: | |
| 67 | + | |
| 68 | +{ mod_muc, [ | |
| 69 | + %%{host, "conference.@HOST@"}, | |
| 70 | + {access, muc}, | |
| 71 | + {access_create, muc}, | |
| 72 | + {access_persistent, muc}, | |
| 73 | + {access_admin, muc_admin}, | |
| 74 | + {max_users, 500}, | |
| 75 | + {default_room_options, [{anonymous, false}]} | |
| 76 | +]}, | |
| 77 | + | |
| 78 | +[1] - http://xmpp.org/extensions/xep-0045.html#enter-nonanon | |
| 79 | + | |
| 80 | + | |
| 81 | + 2.4. Authentication method | |
| 82 | + | |
| 83 | +To use Postgresql through ODBC, the following modifications must be done: | |
| 84 | + | |
| 85 | + * Disable the default method: | |
| 86 | + | |
| 87 | +{auth_method, internal}. | |
| 88 | + | |
| 89 | + * Enable autheticantion through ODBC: | |
| 90 | + | |
| 91 | +{auth_method, odbc}. | |
| 92 | + | |
| 93 | + * Set database server name | |
| 94 | + | |
| 95 | +{odbc_server, "DSN=PostgreSQLEjabberdNoosfero"}. | |
| 96 | + | |
| 97 | + | |
| 98 | + 2.5. Increase the shaper traffic limit | |
| 99 | + | |
| 100 | +{ shaper, normal, { maxrate, 10000000 } }. | |
| 101 | + | |
| 102 | + | |
| 103 | + 2.6. Disable unused modules | |
| 104 | + | |
| 105 | +Unused modules can be disabled, for example: | |
| 106 | + | |
| 107 | + * s2s | |
| 108 | + * web_admin | |
| 109 | + * mod_pubsub | |
| 110 | + * mod_irc | |
| 111 | + * mod_offine | |
| 112 | + * mod_admin_extra | |
| 113 | + * mod_register | |
| 114 | + | |
| 115 | + | |
| 116 | + 2.7. Enable ODBC modules | |
| 117 | + | |
| 118 | + * mod_privacy -> mod_privacy_odbc | |
| 119 | + * mod_private -> mod_private_odbc | |
| 120 | + * mod_roster -> mod_roster_odbc | |
| 121 | + | |
| 122 | +3. Configuring Postgresql | |
| 123 | + | |
| 124 | +Login as noosfero user, and execute: | |
| 125 | + | |
| 126 | + $ psql noosfero < /path/to/noosfero/util/chat/postgresql/ejabberd.sql | |
| 127 | + | |
| 128 | +Where 'noosfero' may need to be replace by the name of the database used for | |
| 129 | +Noosfero. | |
| 130 | + | |
| 131 | +This will create a new schema inside the noosfero database, called 'ejabberd'. | |
| 132 | + | |
| 133 | +Note 'noosfero' user should have permission to create Postgresql schemas. Also, | |
| 134 | +there should be at least one domain with 'is_default = true' in 'domains' | |
| 135 | +table, otherwise people couldn't see your friends online. | |
| 136 | + | |
| 137 | + | |
| 138 | +4. ODBC configuration | |
| 139 | + | |
| 140 | +The following files must be created: | |
| 141 | + | |
| 142 | + * /etc/odbc.ini | |
| 143 | + | |
| 144 | +[PostgreSQLEjabberdNoosfero] | |
| 145 | +Description = PostgreSQL Noosfero ejabberd database | |
| 146 | +Driver = PostgreSQL Unicode | |
| 147 | +Trace = No | |
| 148 | +TraceFile = /tmp/psqlodbc.log | |
| 149 | +Database = noosfero | |
| 150 | +Servername = localhost | |
| 151 | +UserName = <DBUSER> | |
| 152 | +Password = <DBPASS> | |
| 153 | +Port = | |
| 154 | +ReadOnly = No | |
| 155 | +RowVersioning = No | |
| 156 | +ShowSystemTables = No | |
| 157 | +ShowOidColumn = No | |
| 158 | +FakeOidIndex = No | |
| 159 | +ConnSettings = SET search_path TO ejabberd | |
| 160 | + | |
| 161 | + * /etc/odbcinst.ini | |
| 162 | + | |
| 163 | +[PostgreSQL Unicode] | |
| 164 | +Description = PostgreSQL ODBC driver (Unicode version) | |
| 165 | +Driver = /usr/lib/odbc/psqlodbcw.so | |
| 166 | +Setup = /usr/lib/odbc/libodbcpsqlS.so | |
| 167 | +Debug = 0 | |
| 168 | +CommLog = 1 | |
| 169 | +UsageCount = 3 | |
| 170 | + | |
| 171 | + * testing all: | |
| 172 | + | |
| 173 | +# isql 'PostgreSQLEjabberdNoosfero' DBUSER | |
| 174 | + | |
| 175 | + | |
| 176 | +5. Enabling kernel polling and SMP in /etc/default/ejabberd | |
| 177 | + | |
| 178 | +POLL=true | |
| 179 | +SMP=auto | |
| 180 | + | |
| 181 | + | |
| 182 | +6. Increase the file descriptors limit for user ejabberd | |
| 183 | + | |
| 184 | + 6.1. Uncomment this line in file /etc/pam.d/su: | |
| 185 | + | |
| 186 | +session required pam_limits.so | |
| 187 | + | |
| 188 | + | |
| 189 | + 6.2. Add this lines to file /etc/security/limits.conf: | |
| 190 | + | |
| 191 | +ejabberd hard nofile 65536 | |
| 192 | +ejabberd soft nofile 65536 | |
| 193 | + | |
| 194 | +Now, test the configuration: | |
| 195 | + | |
| 196 | +# cat /proc/<EJABBERD_BEAM_PROCESS_PID>/limits | |
| 197 | + | |
| 198 | + | |
| 199 | +7. Apache Configuration | |
| 200 | + | |
| 201 | +Apache server must be configurated as follow: | |
| 202 | + | |
| 203 | + * /etc/apache2/sites-enabled/noosfero | |
| 204 | + | |
| 205 | +RewriteEngine On | |
| 206 | +Include /usr/share/noosfero/util/chat/apache/xmpp.conf | |
| 207 | + | |
| 208 | + * /etc/apache2/apache2.conf: | |
| 209 | + | |
| 210 | +<IfModule mpm_worker_module> | |
| 211 | + StartServers 8 | |
| 212 | + MinSpareThreads 25 | |
| 213 | + MaxSpareThreads 75 | |
| 214 | + ThreadLimit 128 | |
| 215 | + ThreadsPerChild 128 | |
| 216 | + MaxClients 2048 | |
| 217 | + MaxRequestsPerChild 0 | |
| 218 | +</IfModule> | |
| 219 | + | |
| 220 | +Note: module proxy_http must be enabled: | |
| 221 | + | |
| 222 | +# a2enmod proxy_http | |
| 223 | + | |
| 224 | + | |
| 225 | +8. DNS configuration | |
| 226 | + | |
| 227 | + * /etc/bind/db.colivre: | |
| 228 | + | |
| 229 | +_xmpp-client._tcp SRV 5 100 5222 master | |
| 230 | +(...) | |
| 231 | +conference CNAME master | |
| 232 | +_xmpp-client._tcp.conference SRV 5 100 5222 master | |
| 233 | + | |
| 234 | + | |
| 235 | +9. Testing this Setup | |
| 236 | + | |
| 237 | +Adjust shell limits to proceed with some benchmarks and load tests: | |
| 238 | + | |
| 239 | +# ulimit −s 256 | |
| 240 | +# ulimit −n 8192 | |
| 241 | +# echo 10 > /proc/sys/net/ipv4/tcp_syn_retries | |
| 242 | + | |
| 243 | +To measure the bandwidth between server and client: | |
| 244 | + | |
| 245 | + * at server side: | |
| 246 | + | |
| 247 | +# iperf −s | |
| 248 | + | |
| 249 | + * at client side: | |
| 250 | + | |
| 251 | +# iperf −c server_ip | |
| 252 | + | |
| 253 | +For heavy load tests, clone and use this software: | |
| 254 | + | |
| 255 | +git clone http://git.holoscopio.com/git/metal/tester.git | ... | ... |
| ... | ... | @@ -0,0 +1,67 @@ |
| 1 | += Setting up Varnish for your Noosfero site | |
| 2 | + | |
| 3 | +Varnish is a HTTP caching server, and using it together with Noosfero is highly | |
| 4 | +recommended. See http://www.varnish-cache.org/ for more information on Varnish. | |
| 5 | + | |
| 6 | +Varnish can be set up to use with Noosfero with the following steps: | |
| 7 | + | |
| 8 | +1) setup Noosfero with apache according to the INSTALL file. | |
| 9 | + | |
| 10 | +2) install Varnish | |
| 11 | + | |
| 12 | + # apt-get install varnish | |
| 13 | + | |
| 14 | +Noosfero was tested with Varnish 2.x. If you are using a Debian Lenny (and you | |
| 15 | +should, unless Debian already released Squeeze by now), make sure you install | |
| 16 | +varnish from the lenny-backports suite. | |
| 17 | + | |
| 18 | +3) Enable varnish logging: | |
| 19 | + | |
| 20 | +3a) Edit /etc/default/varnishncsa and uncomment the line that contains: | |
| 21 | + | |
| 22 | +VARNISHNCSA_ENABLED=1 | |
| 23 | + | |
| 24 | +The varnish log will be written to /var/log/varnish/varnishncsa.log in an | |
| 25 | +apache-compatible format. You should change your statistics generation software | |
| 26 | +(e.g. awstats) to use that instead of apache logs. | |
| 27 | + | |
| 28 | +3b) Restart Varnish Logging service | |
| 29 | + | |
| 30 | + # invoke-rc.d varnishncsa start | |
| 31 | + | |
| 32 | +4) Change Apache to listen on port 8080 instead of 80 | |
| 33 | + | |
| 34 | +4a) Edit /etc/apache2/ports.conf, and: | |
| 35 | + | |
| 36 | + * change 'Listen 80' to 'Listen 127.0.0.1:8080' | |
| 37 | + * change 'NameVirtualHost *:80' to 'NameVirtualHost *:8080' | |
| 38 | + | |
| 39 | +4b) Edit /etc/apache2/sites-enabled/*, and change '<VirtualHost *:80>' to '<VirtualHost *:8080>' | |
| 40 | + | |
| 41 | +4c) Restart apache | |
| 42 | + | |
| 43 | + # invoke-rc.d apache2 restart | |
| 44 | + | |
| 45 | +5) Change Varnish to listen on port 80 | |
| 46 | + | |
| 47 | +5a) Edit /etc/default/varnish and change '-a :6081' to '-a :80' | |
| 48 | + | |
| 49 | +5b) Restart Varnish | |
| 50 | + | |
| 51 | + # invoke-rc.d varnish restart | |
| 52 | + | |
| 53 | +6) Configure varnish to store separate caches for each language | |
| 54 | + | |
| 55 | +6a) Add the following line to your /etc/varnish/default.vcl file (assuming | |
| 56 | +Noosfero is installed in /var/lib/noosfero): | |
| 57 | + | |
| 58 | + include "/var/lib/noosfero/etc/noosfero/varnish-accept-language.vcl"; | |
| 59 | + | |
| 60 | +6b) Restart Varnish | |
| 61 | + | |
| 62 | + # invoke-rc.d varnish restart | |
| 63 | + | |
| 64 | +Thanks to Cosimo Streppone for varnish-accept-language. See | |
| 65 | +http://github.com/cosimo/varnish-accept-language for more information. | |
| 66 | + | |
| 67 | + -- Antonio Terceiro <terceiro@colivre.coop.br> Sat, 04 Sep 2010 17:29:27 -0300 | ... | ... |
Rakefile.pkg
| ... | ... | @@ -1,101 +0,0 @@ |
| 1 | -require(File.join(File.dirname(__FILE__), 'config', 'boot')) | |
| 2 | - | |
| 3 | -require 'rake' | |
| 4 | -require 'rake/testtask' | |
| 5 | -require 'rake/rdoctask' | |
| 6 | -require 'rake/packagetask' | |
| 7 | -require 'noosfero' | |
| 8 | - | |
| 9 | -Rake::PackageTask.new(Noosfero::PROJECT, Noosfero::VERSION) do |p| | |
| 10 | - p.need_tar_gz = true | |
| 11 | - | |
| 12 | - # cleaning temporary files | |
| 13 | - FileUtils.rm_rf('tmp') | |
| 14 | - FileUtils.mkdir_p('tmp/cache') | |
| 15 | - FileUtils.mkdir_p('tmp/sessions') | |
| 16 | - FileUtils.mkdir_p('tmp/sockets') | |
| 17 | - | |
| 18 | - # application files | |
| 19 | - p.package_files.include('app/**/*.{rb,rhtml,rjs,rxml,erb}') | |
| 20 | - p.package_files.include('config/**/*.{rb,sqlite3}') | |
| 21 | - p.package_files.include('config/ferret_server.yml.dist') | |
| 22 | - p.package_files.include('db/migrate/*.rb') | |
| 23 | - p.package_files.include('db/schema.rb') | |
| 24 | - p.package_files.include('doc/README_FOR_APP') | |
| 25 | - p.package_files.include('lib/**/*.{rake,rb}') | |
| 26 | - p.package_files.include('Rakefile') | |
| 27 | - p.package_files.include('Rakefile.pkg') | |
| 28 | - | |
| 29 | - # translation files | |
| 30 | - p.package_files.include('po/*/*.po') | |
| 31 | - p.package_files.include('po/noosfero.pot') | |
| 32 | - | |
| 33 | - # templates | |
| 34 | - p.package_files.include('public/designs/templates/**/*') | |
| 35 | - | |
| 36 | - # icon sets | |
| 37 | - p.package_files.include('public/designs/icons/tango/**/*') | |
| 38 | - p.package_files.exclude('public/designs/icons/tango/Tango') | |
| 39 | - p.package_files.exclude('public/designs/icons/default') | |
| 40 | - | |
| 41 | - # themes | |
| 42 | - p.package_files.include('public/designs/themes/noosfero/**/*') | |
| 43 | - p.package_files.include('public/designs/themes/base/**/*') | |
| 44 | - p.package_files.exclude('public/designs/themes/default') | |
| 45 | - | |
| 46 | - # static files | |
| 47 | - p.package_files.include('public/dispatch.*') | |
| 48 | - p.package_files.include('public/favicon.ico') | |
| 49 | - p.package_files.include('public/*.html') | |
| 50 | - p.package_files.include('public/*.html.erb') | |
| 51 | - p.package_files.include('public/images/**/*') | |
| 52 | - p.package_files.include('public/javascripts/**/*') | |
| 53 | - p.package_files.include('public/robots.txt') | |
| 54 | - p.package_files.include('public/stylesheets/**/*') | |
| 55 | - | |
| 56 | - # top-level docs | |
| 57 | - p.package_files.include('README') | |
| 58 | - p.package_files.include('COPYING') | |
| 59 | - p.package_files.include('COPYRIGHT') | |
| 60 | - p.package_files.include('INSTALL') | |
| 61 | - p.package_files.include('HACKING') | |
| 62 | - | |
| 63 | - # scripts | |
| 64 | - p.package_files.include('script/**/*') | |
| 65 | - | |
| 66 | - # test files | |
| 67 | - p.package_files.include('test/**/*.{rb,yml}') | |
| 68 | - p.package_files.include('test/fixtures/files/*') | |
| 69 | - p.package_files.include('features/**/*') | |
| 70 | - p.package_files.include('config/cucumber.yml') | |
| 71 | - | |
| 72 | - # empty directories that must exist | |
| 73 | - p.package_files.include('tmp/cache') | |
| 74 | - p.package_files.include('tmp/sessions') | |
| 75 | - p.package_files.include('tmp/sockets') | |
| 76 | - p.package_files.include('log') | |
| 77 | - | |
| 78 | - # symbolic links | |
| 79 | - p.package_files.include('app/views/profile_design/*') | |
| 80 | - p.package_files.include('app/views/environment_design/*') | |
| 81 | - | |
| 82 | - # util | |
| 83 | - p.package_files.include('util/**/*') | |
| 84 | - | |
| 85 | - # external resources | |
| 86 | - p.package_files.include('vendor/**/*') | |
| 87 | - p.package_files.exclude('vendor/rails') | |
| 88 | - | |
| 89 | - # online documentation | |
| 90 | - p.package_files.include('doc/noosfero/**/*') | |
| 91 | - | |
| 92 | - # do not install locally generated files | |
| 93 | - p.package_files.exclude('coverage/**/*') | |
| 94 | - p.package_files.exclude('public/images/[0-9][0-9][0-9][0-9]/**/*') | |
| 95 | - | |
| 96 | -end | |
| 97 | - | |
| 98 | -task :default => :package | |
| 99 | -task :clean => :clobber_package | |
| 100 | - | |
| 101 | -# vim: ft=ruby |
UPGRADE
| ... | ... | @@ -1,49 +0,0 @@ |
| 1 | -0 - All modified code have the tag UPGRADE on it. | |
| 2 | - | |
| 3 | -1 - I Changed acts_as_versioned plugin code to the new one: | |
| 4 | - | |
| 5 | - http://github.com/technoweenie/acts_as_versioned/tree/master | |
| 6 | - | |
| 7 | -2 - I removed the 'build_person' calls after create the user on UserTest | |
| 8 | - | |
| 9 | -3 - The settings_items method generated a lot of bugs. Theses bugs are explained here: | |
| 10 | - Solution 1: http://casperfabricius.com/site/2008/06/12/serialize-doesnt-play-nice-with-rails-21-partial-updates/ | |
| 11 | - Solution 2: http://www.kalzumeus.com/2008/06/28/rails-fails-to-update-serialized-columns-on-save/ | |
| 12 | - | |
| 13 | -The problem is how rails is working with ActiveRecord. The serialization data was not save on database. | |
| 14 | - | |
| 15 | -I add this code | |
| 16 | - | |
| 17 | - send(self.class.settings_field.to_s + '_will_change!') | |
| 18 | - | |
| 19 | -on lib/acts_as_having_settings | |
| 20 | - | |
| 21 | -I used the solution 1. | |
| 22 | - | |
| 23 | -I choose solution number 2 for Environment model. I think it is more clear in this situation. | |
| 24 | -I put the code: | |
| 25 | - | |
| 26 | - self.partial_updates = false | |
| 27 | - | |
| 28 | -on Environment model. | |
| 29 | - | |
| 30 | - | |
| 31 | -4 - acts_as_searchable -> find_by_contents | |
| 32 | - | |
| 33 | -I solved an incompatibility between paginate and geokit with an work around like this the one explained on the link: | |
| 34 | - | |
| 35 | - http://groups.google.com/group/will_paginate/browse_thread/thread/1bbb1f0c5810f1c1 | |
| 36 | - | |
| 37 | -5 - The test of this method 'theme_include' of application helper was not passing. | |
| 38 | - | |
| 39 | -I changed the tests | |
| 40 | - - "should 'render theme footer' do" | |
| 41 | - - " should 'ignore unexisting theme footer' do" | |
| 42 | - | |
| 43 | - to make the test usefull | |
| 44 | - | |
| 45 | - | |
| 46 | -6 - I changed the way as the product is created on ManageProducts controller. I removed the line: | |
| 47 | - @product.build_image unless @product.image | |
| 48 | - | |
| 49 | -because the image was never saved. It don't have valid values to be saved. |
app/controllers/admin/admin_panel_controller.rb
| ... | ... | @@ -40,7 +40,7 @@ class AdminPanelController < AdminController |
| 40 | 40 | end |
| 41 | 41 | redirect_to :action => 'set_portal_folders' |
| 42 | 42 | else |
| 43 | - flash[:notice] = __('Community not found. You must insert the identifier of a community from this environment') | |
| 43 | + session[:notice] = __('Community not found. You must insert the identifier of a community from this environment') | |
| 44 | 44 | end |
| 45 | 45 | end |
| 46 | 46 | end |
| ... | ... | @@ -54,7 +54,7 @@ class AdminPanelController < AdminController |
| 54 | 54 | folders = params[:folders].map{|fid| Folder.find(:first, :conditions => {:profile_id => env.portal_community, :id => fid})} if params[:folders] |
| 55 | 55 | env.portal_folders = folders |
| 56 | 56 | if env.save |
| 57 | - flash[:notice] = _('Saved the portal folders') | |
| 57 | + session[:notice] = _('Saved the portal folders') | |
| 58 | 58 | redirect_to :action => 'set_portal_news_amount' |
| 59 | 59 | end |
| 60 | 60 | end |
| ... | ... | @@ -63,7 +63,7 @@ class AdminPanelController < AdminController |
| 63 | 63 | def set_portal_news_amount |
| 64 | 64 | if request.post? |
| 65 | 65 | if @environment.update_attributes(params[:environment]) |
| 66 | - flash[:notice] = _('Saved the number of news on folders') | |
| 66 | + session[:notice] = _('Saved the number of news on folders') | |
| 67 | 67 | redirect_to :action => 'index' |
| 68 | 68 | end |
| 69 | 69 | end | ... | ... |
app/controllers/admin/categories_controller.rb
| ... | ... | @@ -5,9 +5,9 @@ class CategoriesController < AdminController |
| 5 | 5 | helper :categories |
| 6 | 6 | |
| 7 | 7 | def index |
| 8 | - # WORKAROUND: restricting the category trees to display. Region and | |
| 9 | - # ProductCategory have VERY LARGE trees. | |
| 10 | 8 | @categories = environment.categories.find(:all, :conditions => "parent_id is null AND type is null") |
| 9 | + @regions = environment.regions.find(:all, :conditions => {:parent_id => nil}) | |
| 10 | + @product_categories = environment.product_categories.find(:all, :conditions => {:parent_id => nil}) | |
| 11 | 11 | end |
| 12 | 12 | |
| 13 | 13 | ALLOWED_TYPES = CategoriesHelper::TYPES.map {|item| item[1] } | ... | ... |
app/controllers/admin/environment_design_controller.rb
| ... | ... | @@ -3,7 +3,7 @@ class EnvironmentDesignController < BoxOrganizerController |
| 3 | 3 | protect 'edit_environment_design', :environment |
| 4 | 4 | |
| 5 | 5 | def available_blocks |
| 6 | - @available_blocks ||= [ ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, PeopleBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock ] | |
| 6 | + @available_blocks ||= [ ArticleBlock, LoginBlock, EnvironmentStatisticsBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, PeopleBlock, SellersSearchBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, FeaturedProductsBlock, CategoriesBlock, RawHTMLBlock ] | |
| 7 | 7 | end |
| 8 | 8 | |
| 9 | 9 | end | ... | ... |
app/controllers/admin/environment_role_manager_controller.rb
| ... | ... | @@ -14,9 +14,9 @@ class EnvironmentRoleManagerController < AdminController |
| 14 | 14 | @roles = params[:roles] ? Role.find(params[:roles]) : [] |
| 15 | 15 | @person = Person.find(params[:person]) |
| 16 | 16 | if @person.define_roles(@roles, environment) |
| 17 | - flash[:notice] = _('Roles successfuly updated') | |
| 17 | + session[:notice] = _('Roles successfuly updated') | |
| 18 | 18 | else |
| 19 | - flash[:notice] = _('Couldnt change the roles') | |
| 19 | + session[:notice] = _('Couldnt change the roles') | |
| 20 | 20 | end |
| 21 | 21 | redirect_to :action => :index |
| 22 | 22 | end |
| ... | ... | @@ -42,9 +42,9 @@ class EnvironmentRoleManagerController < AdminController |
| 42 | 42 | def remove_role |
| 43 | 43 | @association = RoleAssignment.find(params[:id]) |
| 44 | 44 | if @association.destroy |
| 45 | - flash[:notice] = _('Member succefully unassociated') | |
| 45 | + session[:notice] = _('Member succefully unassociated') | |
| 46 | 46 | else |
| 47 | - flash[:notice] = _('Failed to unassociate member') | |
| 47 | + session[:notice] = _('Failed to unassociate member') | |
| 48 | 48 | end |
| 49 | 49 | redirect_to :aciton => 'index' |
| 50 | 50 | end |
| ... | ... | @@ -52,9 +52,9 @@ class EnvironmentRoleManagerController < AdminController |
| 52 | 52 | def unassociate |
| 53 | 53 | @association = RoleAssignment.find(params[:id]) |
| 54 | 54 | if @association.destroy |
| 55 | - flash[:notice] = _('Member succefully unassociated') | |
| 55 | + session[:notice] = _('Member succefully unassociated') | |
| 56 | 56 | else |
| 57 | - flash[:notice] = _('Failed to unassociate member') | |
| 57 | + session[:notice] = _('Failed to unassociate member') | |
| 58 | 58 | end |
| 59 | 59 | redirect_to :aciton => 'index' |
| 60 | 60 | end | ... | ... |
app/controllers/admin/features_controller.rb
| ... | ... | @@ -8,7 +8,7 @@ class FeaturesController < AdminController |
| 8 | 8 | post_only :update |
| 9 | 9 | def update |
| 10 | 10 | if @environment.update_attributes(params[:environment]) |
| 11 | - flash[:notice] = _('Features updated successfully.') | |
| 11 | + session[:notice] = _('Features updated successfully.') | |
| 12 | 12 | redirect_to :action => 'index' |
| 13 | 13 | else |
| 14 | 14 | render :action => 'index' |
| ... | ... | @@ -24,7 +24,7 @@ class FeaturesController < AdminController |
| 24 | 24 | def manage_person_fields |
| 25 | 25 | environment.custom_person_fields = params[:person_fields] |
| 26 | 26 | if environment.save! |
| 27 | - flash[:notice] = _('Person fields updated successfully.') | |
| 27 | + session[:notice] = _('Person fields updated successfully.') | |
| 28 | 28 | else |
| 29 | 29 | flash[:error] = _('Person fields not updated successfully.') |
| 30 | 30 | end |
| ... | ... | @@ -34,9 +34,9 @@ class FeaturesController < AdminController |
| 34 | 34 | def manage_enterprise_fields |
| 35 | 35 | environment.custom_enterprise_fields = params[:enterprise_fields] |
| 36 | 36 | if environment.save! |
| 37 | - flash[:notice] = _('Enterprise fields updated successfully.') | |
| 37 | + session[:notice] = __('Enterprise fields updated successfully.') | |
| 38 | 38 | else |
| 39 | - flash[:error] = _('Enterprise fields not updated successfully.') | |
| 39 | + flash[:error] = __('Enterprise fields not updated successfully.') | |
| 40 | 40 | end |
| 41 | 41 | redirect_to :action => 'manage_fields' |
| 42 | 42 | end |
| ... | ... | @@ -44,7 +44,7 @@ class FeaturesController < AdminController |
| 44 | 44 | def manage_community_fields |
| 45 | 45 | environment.custom_community_fields = params[:community_fields] |
| 46 | 46 | if environment.save! |
| 47 | - flash[:notice] = _('Community fields updated successfully.') | |
| 47 | + session[:notice] = _('Community fields updated successfully.') | |
| 48 | 48 | else |
| 49 | 49 | flash[:error] = _('Community fields not updated successfully.') |
| 50 | 50 | end | ... | ... |
| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | +class PluginsController < AdminController | |
| 2 | + | |
| 3 | + def index | |
| 4 | + @active_plugins = Noosfero::Plugin.all.map {|plugin_name| plugin_name.constantize }.compact | |
| 5 | + end | |
| 6 | + | |
| 7 | + post_only :update | |
| 8 | + def update | |
| 9 | + params[:environment][:enabled_plugins].delete('') | |
| 10 | + if @environment.update_attributes(params[:environment]) | |
| 11 | + session[:notice] = _('Plugins updated successfully.') | |
| 12 | + else | |
| 13 | + session[:error] = _('Plugins were not updated successfully.') | |
| 14 | + end | |
| 15 | + redirect_to :action => 'index' | |
| 16 | + end | |
| 17 | + | |
| 18 | +end | ... | ... |
app/controllers/admin/role_controller.rb
| ... | ... | @@ -19,7 +19,7 @@ class RoleController < AdminController |
| 19 | 19 | if @role.save |
| 20 | 20 | redirect_to :action => 'show', :id => @role |
| 21 | 21 | else |
| 22 | - flash[:notice] = _('Failed to create role') | |
| 22 | + session[:notice] = _('Failed to create role') | |
| 23 | 23 | render :action => 'new' |
| 24 | 24 | end |
| 25 | 25 | end |
| ... | ... | @@ -33,7 +33,7 @@ class RoleController < AdminController |
| 33 | 33 | if @role.update_attributes(params[:role]) |
| 34 | 34 | redirect_to :action => 'show', :id => @role |
| 35 | 35 | else |
| 36 | - flash[:notice] = _('Failed to edit role') | |
| 36 | + session[:notice] = _('Failed to edit role') | |
| 37 | 37 | render :action => 'edit' |
| 38 | 38 | end |
| 39 | 39 | end |
| ... | ... | @@ -43,7 +43,7 @@ class RoleController < AdminController |
| 43 | 43 | if @role.destroy |
| 44 | 44 | redirect_to :action => 'index' |
| 45 | 45 | else |
| 46 | - flash[:notice] = _('Failed to edit role') | |
| 46 | + session[:notice] = _('Failed to edit role') | |
| 47 | 47 | redirect_to :action => 'index' |
| 48 | 48 | end |
| 49 | 49 | end | ... | ... |
| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | +class UsersController < AdminController | |
| 2 | + | |
| 3 | + protect 'manage_environment_users', :environment | |
| 4 | + | |
| 5 | + def index | |
| 6 | + @users = environment.users | |
| 7 | + respond_to do |format| | |
| 8 | + format.html | |
| 9 | + format.xml do | |
| 10 | + render :xml => @users.to_xml( | |
| 11 | + :skip_types => true, | |
| 12 | + :only => %w[email login created_at updated_at], | |
| 13 | + :include => { :person => {:only => %w[name updated_at created_at address birth_date contact_phone identifier lat lng] } } | |
| 14 | + ) | |
| 15 | + end | |
| 16 | + format.csv do | |
| 17 | + render :template => "users/index_csv.rhtml", :content_type => 'text/csv', :layout => false | |
| 18 | + end | |
| 19 | + end | |
| 20 | + end | |
| 21 | + | |
| 22 | + def send_mail | |
| 23 | + @mailing = environment.mailings.build(params[:mailing]) | |
| 24 | + if request.post? | |
| 25 | + @mailing.locale = locale | |
| 26 | + @mailing.person = user | |
| 27 | + if @mailing.save | |
| 28 | + session[:notice] = _('The e-mails are being sent') | |
| 29 | + redirect_to :action => 'index' | |
| 30 | + else | |
| 31 | + session[:notice] = _('Could not create the e-mail') | |
| 32 | + end | |
| 33 | + end | |
| 34 | + end | |
| 35 | + | |
| 36 | +end | ... | ... |
app/controllers/box_organizer_controller.rb
| ... | ... | @@ -96,7 +96,7 @@ class BoxOrganizerController < ApplicationController |
| 96 | 96 | expire_timeout_fragment(@block.cache_keys) |
| 97 | 97 | redirect_to :action => 'index' |
| 98 | 98 | else |
| 99 | - flash[:notice] = _('Failed to remove block') | |
| 99 | + session[:notice] = _('Failed to remove block') | |
| 100 | 100 | end |
| 101 | 101 | end |
| 102 | 102 | ... | ... |
app/controllers/my_profile/cms_controller.rb
| ... | ... | @@ -14,7 +14,8 @@ class CmsController < MyProfileController |
| 14 | 14 | end |
| 15 | 15 | end |
| 16 | 16 | |
| 17 | - protect_if :except => [:set_home_page, :edit, :destroy, :publish] do |c, user, profile| | |
| 17 | + before_filter :login_required, :except => [:suggest_an_article] | |
| 18 | + protect_if :except => [:suggest_an_article, :set_home_page, :edit, :destroy, :publish] do |c, user, profile| | |
| 18 | 19 | user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)) |
| 19 | 20 | end |
| 20 | 21 | |
| ... | ... | @@ -40,15 +41,11 @@ class CmsController < MyProfileController |
| 40 | 41 | def available_article_types |
| 41 | 42 | articles = [ |
| 42 | 43 | TinyMceArticle, |
| 43 | - TextileArticle | |
| 44 | + TextileArticle, | |
| 45 | + Event | |
| 44 | 46 | ] |
| 45 | - articles << Event unless profile.environment.enabled?(:disable_asset_events) | |
| 47 | + articles += special_article_types if params && params[:cms] | |
| 46 | 48 | parent_id = params ? params[:parent_id] : nil |
| 47 | - if !parent_id or !Article.find(parent_id).blog? | |
| 48 | - articles += [ | |
| 49 | - RssFeed | |
| 50 | - ] | |
| 51 | - end | |
| 52 | 49 | if profile.enterprise? |
| 53 | 50 | articles << EnterpriseHomepage |
| 54 | 51 | end |
| ... | ... | @@ -56,22 +53,31 @@ class CmsController < MyProfileController |
| 56 | 53 | end |
| 57 | 54 | |
| 58 | 55 | def special_article_types |
| 59 | - [Folder, Blog, UploadedFile] | |
| 56 | + [Folder, Blog, UploadedFile, Forum, Gallery, RssFeed] | |
| 60 | 57 | end |
| 61 | 58 | |
| 62 | 59 | def view |
| 63 | 60 | @article = profile.articles.find(params[:id]) |
| 64 | - @subitems = @article.children.reject {|item| item.folder? } | |
| 65 | - if @article.blog? | |
| 66 | - @subitems.reject! {|item| item.class == RssFeed } | |
| 61 | + conditions = [] | |
| 62 | + if @article.has_posts? | |
| 63 | + conditions = ['type != ?', 'RssFeed'] | |
| 67 | 64 | end |
| 68 | - @folders = @article.children.select {|item| item.folder? } | |
| 65 | + | |
| 66 | + @articles = @article.children.paginate( | |
| 67 | + :order => "case when type = 'Folder' then 0 when type ='Blog' then 1 else 2 end, updated_at DESC", | |
| 68 | + :conditions => conditions, | |
| 69 | + :per_page => per_page, | |
| 70 | + :page => params[:npage] | |
| 71 | + ) | |
| 69 | 72 | end |
| 70 | 73 | |
| 71 | 74 | def index |
| 72 | 75 | @article = nil |
| 73 | - @subitems = profile.top_level_articles.reject {|item| item.folder? } | |
| 74 | - @folders = profile.top_level_articles.select {|item| item.folder?} | |
| 76 | + @articles = profile.top_level_articles.paginate( | |
| 77 | + :order => "case when type = 'Folder' then 0 when type ='Blog' then 1 else 2 end, updated_at DESC", | |
| 78 | + :per_page => per_page, | |
| 79 | + :page => params[:npage] | |
| 80 | + ) | |
| 75 | 81 | render :action => 'view' |
| 76 | 82 | end |
| 77 | 83 | |
| ... | ... | @@ -79,17 +85,21 @@ class CmsController < MyProfileController |
| 79 | 85 | @article = profile.articles.find(params[:id]) |
| 80 | 86 | @parent_id = params[:parent_id] |
| 81 | 87 | @type = params[:type] || @article.class.to_s |
| 88 | + translations if @article.translatable? | |
| 89 | + continue = params[:continue] | |
| 82 | 90 | |
| 83 | 91 | refuse_blocks |
| 84 | - if !@article.nil? && @article.blog? || !@type.nil? && @type == 'Blog' | |
| 85 | - @back_url = url_for(:controller => 'profile_editor', :profile => profile.identifier) | |
| 86 | - end | |
| 87 | - record_coming_from_public_view | |
| 92 | + record_coming | |
| 88 | 93 | if request.post? |
| 89 | 94 | @article.last_changed_by = user |
| 90 | 95 | if @article.update_attributes(params[:article]) |
| 91 | - redirect_back | |
| 92 | - return | |
| 96 | + if !continue | |
| 97 | + if @article.content_type.nil? || @article.image? | |
| 98 | + redirect_to @article.view_url | |
| 99 | + else | |
| 100 | + redirect_to :action => (@article.parent ? 'view' : 'index'), :id => @article.parent | |
| 101 | + end | |
| 102 | + end | |
| 93 | 103 | end |
| 94 | 104 | end |
| 95 | 105 | end |
| ... | ... | @@ -99,6 +109,7 @@ class CmsController < MyProfileController |
| 99 | 109 | |
| 100 | 110 | # user must choose an article type first |
| 101 | 111 | |
| 112 | + record_coming | |
| 102 | 113 | @type = params[:type] |
| 103 | 114 | if @type.blank? |
| 104 | 115 | @article_types = [] |
| ... | ... | @@ -110,12 +121,9 @@ class CmsController < MyProfileController |
| 110 | 121 | }) |
| 111 | 122 | end |
| 112 | 123 | @parent_id = params[:parent_id] |
| 113 | - render :action => 'select_article_type', :layout => false | |
| 124 | + render :action => 'select_article_type', :layout => false, :back_to => @back_to | |
| 114 | 125 | return |
| 115 | 126 | else |
| 116 | - if @type == 'Blog' | |
| 117 | - @back_url = url_for(:controller => 'profile_editor', :profile => profile.identifier) | |
| 118 | - end | |
| 119 | 127 | refuse_blocks |
| 120 | 128 | end |
| 121 | 129 | |
| ... | ... | @@ -131,13 +139,19 @@ class CmsController < MyProfileController |
| 131 | 139 | @parent_id = parent.id |
| 132 | 140 | end |
| 133 | 141 | |
| 134 | - record_creating_from_public_view | |
| 142 | + translations if @article.translatable? | |
| 135 | 143 | |
| 136 | 144 | @article.profile = profile |
| 137 | 145 | @article.last_changed_by = user |
| 146 | + | |
| 147 | + continue = params[:continue] | |
| 138 | 148 | if request.post? |
| 139 | 149 | if @article.save |
| 140 | - redirect_back | |
| 150 | + if continue | |
| 151 | + redirect_to :action => 'edit', :id => @article | |
| 152 | + else | |
| 153 | + redirect_to @article.view_url | |
| 154 | + end | |
| 141 | 155 | return |
| 142 | 156 | end |
| 143 | 157 | end |
| ... | ... | @@ -150,7 +164,7 @@ class CmsController < MyProfileController |
| 150 | 164 | @article = profile.articles.find(params[:id]) |
| 151 | 165 | profile.home_page = @article |
| 152 | 166 | profile.save(false) |
| 153 | - flash[:notice] = _('"%s" configured as home page.') % @article.name | |
| 167 | + session[:notice] = _('"%s" configured as home page.') % @article.name | |
| 154 | 168 | redirect_to :action => 'view', :id => @article.id |
| 155 | 169 | end |
| 156 | 170 | |
| ... | ... | @@ -159,29 +173,36 @@ class CmsController < MyProfileController |
| 159 | 173 | @article = @parent = check_parent(params[:parent_id]) |
| 160 | 174 | @target = @parent ? ('/%s/%s' % [profile.identifier, @parent.full_name]) : '/%s' % profile.identifier |
| 161 | 175 | @folders = Folder.find(:all, :conditions => { :profile_id => profile }) |
| 162 | - record_coming_from_public_view if @article | |
| 176 | + @media_listing = params[:media_listing] | |
| 177 | + if @article && !@media_listing | |
| 178 | + record_coming | |
| 179 | + end | |
| 163 | 180 | if request.post? && params[:uploaded_files] |
| 164 | 181 | params[:uploaded_files].each do |file| |
| 165 | 182 | @uploaded_files << UploadedFile.create(:uploaded_data => file, :profile => profile, :parent => @parent) unless file == '' |
| 166 | 183 | end |
| 167 | 184 | @errors = @uploaded_files.select { |f| f.errors.any? } |
| 168 | - @back_to = params[:back_to] | |
| 169 | 185 | if @errors.any? |
| 170 | - if @back_to && @back_to == 'media_listing' | |
| 186 | + if @media_listing | |
| 171 | 187 | flash[:notice] = _('Could not upload all files') |
| 172 | - redirect_back | |
| 188 | + redirect_to :action => 'media_listing' | |
| 173 | 189 | else |
| 174 | 190 | render :action => 'upload_files', :parent_id => @parent_id |
| 175 | 191 | end |
| 176 | 192 | else |
| 177 | - if params[:back_to] | |
| 178 | - redirect_back | |
| 193 | + if @media_listing | |
| 194 | + flash[:notice] = _('All files were uploaded successfully') | |
| 195 | + redirect_to :action => 'media_listing' | |
| 179 | 196 | else |
| 180 | - redirect_to( if @parent | |
| 181 | - {:action => 'view', :id => @parent.id} | |
| 197 | + if @back_to | |
| 198 | + redirect_to @back_to | |
| 182 | 199 | else |
| 183 | - {:action => 'index'} | |
| 184 | - end) | |
| 200 | + redirect_to( if @parent | |
| 201 | + {:action => 'view', :id => @parent.id} | |
| 202 | + else | |
| 203 | + {:action => 'index'} | |
| 204 | + end) | |
| 205 | + end | |
| 185 | 206 | end |
| 186 | 207 | end |
| 187 | 208 | end |
| ... | ... | @@ -212,11 +233,11 @@ class CmsController < MyProfileController |
| 212 | 233 | |
| 213 | 234 | def publish |
| 214 | 235 | @article = profile.articles.find(params[:id]) |
| 215 | - record_coming_from_public_view | |
| 236 | + record_coming | |
| 216 | 237 | @groups = profile.memberships - [profile] |
| 217 | 238 | @marked_groups = [] |
| 218 | 239 | groups_ids = profile.memberships.map{|m|m.id.to_s} |
| 219 | - @marked_groups = params[:marked_groups].map do |item| | |
| 240 | + @marked_groups = params[:marked_groups].map do |key, item| | |
| 220 | 241 | if groups_ids.include?(item[:group_id]) |
| 221 | 242 | item.merge :group => Profile.find(item.delete(:group_id)) |
| 222 | 243 | end |
| ... | ... | @@ -232,8 +253,47 @@ class CmsController < MyProfileController |
| 232 | 253 | end |
| 233 | 254 | end |
| 234 | 255 | if @failed.blank? |
| 235 | - flash[:notice] = _("Your publish request was sent successfully") | |
| 236 | - redirect_back | |
| 256 | + session[:notice] = _("Your publish request was sent successfully") | |
| 257 | + if @back_to | |
| 258 | + redirect_to @back_to | |
| 259 | + else | |
| 260 | + redirect_to @article.view_url | |
| 261 | + end | |
| 262 | + end | |
| 263 | + end | |
| 264 | + end | |
| 265 | + | |
| 266 | + def publish_on_portal_community | |
| 267 | + @article = profile.articles.find(params[:id]) | |
| 268 | + if request.post? | |
| 269 | + if environment.portal_community | |
| 270 | + task = ApproveArticle.create!(:article => @article, :name => params[:name], :target => environment.portal_community, :requestor => user) | |
| 271 | + begin | |
| 272 | + task.finish unless environment.portal_community.moderated_articles? | |
| 273 | + flash[:notice] = _("Your publish request was sent successfully") | |
| 274 | + rescue | |
| 275 | + flash[:error] = _("Your publish request couldn't be sent.") | |
| 276 | + end | |
| 277 | + else | |
| 278 | + flash[:notice] = _("There is no portal community to publish your article.") | |
| 279 | + end | |
| 280 | + | |
| 281 | + if @back_to | |
| 282 | + redirect_to @back_to | |
| 283 | + else | |
| 284 | + redirect_to @article.view_url | |
| 285 | + end | |
| 286 | + end | |
| 287 | + end | |
| 288 | + | |
| 289 | + def suggest_an_article | |
| 290 | + @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url) | |
| 291 | + @task = SuggestArticle.new(params[:task]) | |
| 292 | + if request.post? | |
| 293 | + @task.target = profile | |
| 294 | + if @task.save | |
| 295 | + session[:notice] = _('Thanks for your suggestion. The community administrators were notified.') | |
| 296 | + redirect_to @back_to | |
| 237 | 297 | end |
| 238 | 298 | end |
| 239 | 299 | end |
| ... | ... | @@ -241,24 +301,24 @@ class CmsController < MyProfileController |
| 241 | 301 | def media_listing |
| 242 | 302 | if params[:image_folder_id] |
| 243 | 303 | folder = profile.articles.find(params[:image_folder_id]) if !params[:image_folder_id].blank? |
| 244 | - @images = (folder ? folder.children : UploadedFile.find(:all, :order => 'created_at desc', :conditions => ["profile_id = ? AND parent_id is NULL", profile ])).select { |c| c.image? } | |
| 304 | + @images = (folder ? folder.children : profile.top_level_articles).images | |
| 245 | 305 | elsif params[:document_folder_id] |
| 246 | 306 | folder = profile.articles.find(params[:document_folder_id]) if !params[:document_folder_id].blank? |
| 247 | - @documents = (folder ? folder.children : UploadedFile.find(:all, :order => 'created_at desc', :conditions => ["profile_id = ? AND parent_id is NULL", profile ])).select { |c| c.kind_of?(UploadedFile) && !c.image? } | |
| 307 | + @documents = (folder ? folder.children : profile.top_level_articles) | |
| 248 | 308 | else |
| 249 | - @documents = UploadedFile.find(:all, :order => 'created_at desc', :conditions => ["profile_id = ? AND parent_id is NULL", profile ]) | |
| 250 | - @images = @documents.select(&:image?) | |
| 309 | + @documents = profile.articles | |
| 310 | + @images = @documents.images | |
| 251 | 311 | @documents -= @images |
| 252 | 312 | end |
| 253 | 313 | |
| 254 | - @images = @images.paginate(:per_page => per_page, :page => params[:ipage]) if @images | |
| 255 | - @documents = @documents.paginate(:per_page => per_page, :page => params[:dpage]) if @documents | |
| 314 | + @images = @images.paginate(:per_page => per_page, :page => params[:ipage], :order => "updated_at desc") if @images | |
| 315 | + @documents = @documents.paginate(:per_page => per_page, :page => params[:dpage], :order => "updated_at desc", :conditions => {:is_image => false}) if @documents | |
| 256 | 316 | |
| 257 | - @folders = Folder.find(:all, :conditions => { :profile_id => profile }) | |
| 317 | + @folders = profile.folders | |
| 258 | 318 | @image_folders = @folders.select {|f| f.children.any? {|c| c.image?} } |
| 259 | 319 | @document_folders = @folders.select {|f| f.children.any? {|c| !c.image? && c.kind_of?(UploadedFile) } } |
| 260 | 320 | |
| 261 | - @back_to = 'media_listing' | |
| 321 | + @media_listing = true | |
| 262 | 322 | |
| 263 | 323 | respond_to do |format| |
| 264 | 324 | format.html { render :layout => false} |
| ... | ... | @@ -273,42 +333,11 @@ class CmsController < MyProfileController |
| 273 | 333 | |
| 274 | 334 | protected |
| 275 | 335 | |
| 276 | - def redirect_back | |
| 277 | - if params[:back_to] == 'control_panel' | |
| 278 | - redirect_to :controller => 'profile_editor', :profile => @profile.identifier | |
| 279 | - elsif params[:back_to] == 'public_view' | |
| 280 | - redirect_to @article.view_url.merge(Noosfero.url_options) | |
| 281 | - elsif params[:back_to] == 'media_listing' | |
| 282 | - redirect_to :action => 'media_listing' | |
| 283 | - elsif @article.parent | |
| 284 | - redirect_to :action => 'view', :id => @article.parent | |
| 285 | - elsif @article.folder? && !@article.blog? && @article.parent | |
| 286 | - redirect_to :action => 'index' | |
| 336 | + def record_coming | |
| 337 | + if request.post? | |
| 338 | + @back_to = params[:back_to] | |
| 287 | 339 | else |
| 288 | - redirect_back_or_default :action => 'index' | |
| 289 | - end | |
| 290 | - end | |
| 291 | - | |
| 292 | - def record_coming_from_public_view | |
| 293 | - referer = request.referer | |
| 294 | - referer.gsub!(/\?.*/, '') unless referer.nil? | |
| 295 | - if (maybe_ssl(url_for(@article.url)).include?(referer)) || (@article == profile.home_page && maybe_ssl(url_for(profile.url)).include?(referer)) | |
| 296 | - @back_to = 'public_view' | |
| 297 | - @back_url = @article.view_url | |
| 298 | - end | |
| 299 | - if !request.post? and @article.blog? | |
| 300 | - store_location(request.referer) | |
| 301 | - end | |
| 302 | - end | |
| 303 | - | |
| 304 | - def record_creating_from_public_view | |
| 305 | - referer = request.referer | |
| 306 | - if (referer =~ Regexp.new("^#{(url_for(profile.url).sub('https:', 'https?:'))}")) || params[:back_to] == 'public_view' | |
| 307 | - @back_to = 'public_view' | |
| 308 | - @back_url = referer | |
| 309 | - end | |
| 310 | - if !request.post? and @article.blog? | |
| 311 | - store_location(request.referer) | |
| 340 | + @back_to = params[:back_to] || request.referer | |
| 312 | 341 | end |
| 313 | 342 | end |
| 314 | 343 | |
| ... | ... | @@ -341,5 +370,11 @@ class CmsController < MyProfileController |
| 341 | 370 | def per_page |
| 342 | 371 | 10 |
| 343 | 372 | end |
| 373 | + | |
| 374 | + def translations | |
| 375 | + @locales = Noosfero.locales.invert.reject { |name, lang| !@article.possible_translations.include?(lang) } | |
| 376 | + @selected_locale = @article.language || FastGettext.locale | |
| 377 | + end | |
| 378 | + | |
| 344 | 379 | end |
| 345 | 380 | ... | ... |
app/controllers/my_profile/friends_controller.rb
| ... | ... | @@ -8,18 +8,6 @@ class FriendsController < MyProfileController |
| 8 | 8 | end |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | - def add | |
| 12 | - @friend = Person.find(params[:id]) | |
| 13 | - if request.post? && params[:confirmation] | |
| 14 | - # FIXME this shouldn't be in Person model? | |
| 15 | - AddFriend.create!(:person => profile, :friend => @friend, :group_for_person => params[:group]) | |
| 16 | - | |
| 17 | - flash[:notice] = _('%s still needs to accept being your friend.') % @friend.name | |
| 18 | - # FIXME shouldn't redirect to the friend's page? | |
| 19 | - redirect_to :action => 'index' | |
| 20 | - end | |
| 21 | - end | |
| 22 | - | |
| 23 | 11 | def remove |
| 24 | 12 | @friend = profile.friends.find(params[:id]) |
| 25 | 13 | if request.post? && params[:confirmation] | ... | ... |
app/controllers/my_profile/mailconf_controller.rb
| ... | ... | @@ -20,20 +20,20 @@ class MailconfController < MyProfileController |
| 20 | 20 | @task = EmailActivation.new(:target => environment, :requestor => profile) |
| 21 | 21 | begin |
| 22 | 22 | @task.save! |
| 23 | - flash[:notice] = _('Please fill your personal information below in order to get your mailbox approved by one of the administrators') | |
| 23 | + session[:notice] = _('Please fill your personal information below in order to get your mailbox approved by one of the administrators') | |
| 24 | 24 | redirect_to :controller => 'profile_editor', :action => 'edit' |
| 25 | 25 | rescue Exception => ex |
| 26 | - flash[:notice] = _('e-Mail was not enabled successfully.') | |
| 26 | + session[:notice] = _('e-Mail was not enabled successfully.') | |
| 27 | 27 | render :action => 'index' |
| 28 | 28 | end |
| 29 | 29 | end |
| 30 | 30 | post_only :disable |
| 31 | 31 | def disable |
| 32 | 32 | if profile.user.disable_email! |
| 33 | - flash[:notice] = _('e-Mail disabled successfully.') | |
| 33 | + session[:notice] = _('e-Mail disabled successfully.') | |
| 34 | 34 | redirect_to :controller => 'profile_editor' |
| 35 | 35 | else |
| 36 | - flash[:notice] = _('e-Mail was not disabled successfully.') | |
| 36 | + session[:notice] = _('e-Mail was not disabled successfully.') | |
| 37 | 37 | redirect_to :action => 'index' |
| 38 | 38 | end |
| 39 | 39 | end | ... | ... |
app/controllers/my_profile/manage_products_controller.rb
| 1 | 1 | class ManageProductsController < ApplicationController |
| 2 | 2 | needs_profile |
| 3 | 3 | |
| 4 | - protect 'manage_products', :profile | |
| 4 | + protect 'manage_products', :profile, :except => [:show] | |
| 5 | 5 | before_filter :check_environment_feature |
| 6 | + before_filter :login_required, :except => [:show] | |
| 6 | 7 | |
| 7 | 8 | protected |
| 9 | + | |
| 8 | 10 | def check_environment_feature |
| 9 | 11 | if profile.environment.enabled?('disable_products_for_enterprises') |
| 10 | 12 | render_not_found |
| ... | ... | @@ -13,104 +15,147 @@ class ManageProductsController < ApplicationController |
| 13 | 15 | end |
| 14 | 16 | |
| 15 | 17 | public |
| 18 | + | |
| 16 | 19 | def index |
| 17 | - @products = @profile.products | |
| 18 | - @consumptions = @profile.consumptions | |
| 20 | + @products = @profile.products.paginate(:per_page => 10, :page => params[:page]) | |
| 19 | 21 | end |
| 20 | 22 | |
| 21 | 23 | def show |
| 22 | 24 | @product = @profile.products.find(params[:id]) |
| 25 | + @inputs = @product.inputs | |
| 26 | + @allowed_user = user && user.has_permission?('manage_products', profile) | |
| 27 | + end | |
| 28 | + | |
| 29 | + def categories_for_selection | |
| 30 | + @category = Category.find(params[:category_id]) if params[:category_id] | |
| 31 | + @object_name = params[:object_name] | |
| 32 | + if @category | |
| 33 | + @categories = @category.children | |
| 34 | + @level = @category.leaf? ? @category.level : @categories.first.level | |
| 35 | + else | |
| 36 | + @categories = ProductCategory.top_level_for(environment) | |
| 37 | + @level = 0 | |
| 38 | + end | |
| 39 | + render :partial => 'categories_for_selection', :locals => { :categories => @categories, :level => @level } | |
| 23 | 40 | end |
| 24 | 41 | |
| 25 | 42 | def new |
| 26 | - @object = Product.new | |
| 27 | - @categories = @current_category.nil? ? ProductCategory.top_level_for(environment) : @current_category.children | |
| 28 | - @product = @profile.products.build(params[:product]) | |
| 43 | + @product = @profile.products.build(:product_category_id => params[:selected_category_id]) | |
| 44 | + @category = @product.product_category | |
| 45 | + @categories = ProductCategory.top_level_for(environment) | |
| 46 | + @level = 0 | |
| 29 | 47 | if request.post? |
| 30 | 48 | if @product.save |
| 31 | - flash[:notice] = _('Product succesfully created') | |
| 32 | - redirect_to :action => 'show', :id => @product | |
| 49 | + session[:notice] = _('Product succesfully created') | |
| 50 | + render :partial => 'shared/redirect_via_javascript', | |
| 51 | + :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) } | |
| 33 | 52 | else |
| 34 | - flash[:notice] = _('Could not create the product') | |
| 53 | + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } | |
| 35 | 54 | end |
| 36 | 55 | end |
| 37 | 56 | end |
| 38 | 57 | |
| 39 | 58 | def edit |
| 40 | 59 | @product = @profile.products.find(params[:id]) |
| 41 | - @object = @profile.products.find(params[:id]) | |
| 42 | - @current_category = @product.product_category | |
| 43 | - @categories = @current_category.nil? ? [] : @current_category.children | |
| 60 | + field = params[:field] | |
| 61 | + if request.post? | |
| 62 | + begin | |
| 63 | + @product.update_attributes!(params[:product]) | |
| 64 | + render :partial => "display_#{field}", :locals => {:product => @product} | |
| 65 | + rescue Exception => e | |
| 66 | + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => true} | |
| 67 | + end | |
| 68 | + else | |
| 69 | + render :partial => "edit_#{field}", :locals => {:product => @product, :errors => false} | |
| 70 | + end | |
| 71 | + end | |
| 72 | + | |
| 73 | + def edit_category | |
| 74 | + @product = @profile.products.find(params[:id]) | |
| 75 | + @category = @product.product_category | |
| 76 | + @categories = ProductCategory.top_level_for(environment) | |
| 77 | + @edit = true | |
| 78 | + @level = @category.level | |
| 79 | + if request.post? | |
| 80 | + if @product.update_attributes(:product_category_id => params[:selected_category_id]) | |
| 81 | + render :partial => 'shared/redirect_via_javascript', | |
| 82 | + :locals => { :url => url_for(:controller => 'manage_products', :action => 'show', :id => @product) } | |
| 83 | + else | |
| 84 | + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } | |
| 85 | + end | |
| 86 | + end | |
| 87 | + end | |
| 88 | + | |
| 89 | + def add_input | |
| 90 | + @product = @profile.products.find(params[:id]) | |
| 91 | + @input = @product.inputs.build | |
| 92 | + @categories = ProductCategory.top_level_for(environment) | |
| 93 | + @level = 0 | |
| 44 | 94 | if request.post? |
| 45 | - if @product.update_attributes(params[:product]) | |
| 46 | - flash[:notice] = _('Product succesfully updated') | |
| 47 | - redirect_back_or_default :action => 'show', :id => @product | |
| 95 | + if @input.update_attributes(:product_category_id => params[:selected_category_id]) | |
| 96 | + @inputs = @product.inputs | |
| 97 | + render :partial => 'display_inputs' | |
| 48 | 98 | else |
| 49 | - flash[:notice] = _('Could not update the product') | |
| 99 | + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'product' } | |
| 50 | 100 | end |
| 101 | + else | |
| 102 | + render :partial => 'add_input' | |
| 51 | 103 | end |
| 52 | 104 | end |
| 53 | 105 | |
| 54 | 106 | def destroy |
| 55 | 107 | @product = @profile.products.find(params[:id]) |
| 56 | 108 | if @product.destroy |
| 57 | - flash[:notice] = _('Product succesfully removed') | |
| 109 | + session[:notice] = _('Product succesfully removed') | |
| 58 | 110 | redirect_back_or_default :action => 'index' |
| 59 | 111 | else |
| 60 | - flash[:notice] = _('Could not remove the product') | |
| 112 | + session[:notice] = _('Could not remove the product') | |
| 61 | 113 | redirect_back_or_default :action => 'show', :id => @product |
| 62 | 114 | end |
| 63 | 115 | end |
| 64 | 116 | |
| 65 | - def update_categories | |
| 66 | - @object = params[:id] ? @profile.products.find(params[:id]) : Product.new | |
| 67 | - if params[:category_id] | |
| 68 | - @current_category = Category.find(params[:category_id]) | |
| 69 | - @categories = @current_category.children | |
| 70 | - else | |
| 71 | - @current_category = ProductCategory.top_level_for(environment).first | |
| 72 | - @categories = @current_category.nil? ? [] : @current_category.children | |
| 117 | + def edit_input | |
| 118 | + if request.xhr? | |
| 119 | + @input = @profile.inputs.find_by_id(params[:id]) | |
| 120 | + if @input | |
| 121 | + if request.post? | |
| 122 | + if @input.update_attributes(params[:input]) | |
| 123 | + render :partial => 'display_input', :locals => {:input => @input} | |
| 124 | + else | |
| 125 | + render :partial => 'edit_input' | |
| 126 | + end | |
| 127 | + else | |
| 128 | + render :partial => 'edit_input' | |
| 129 | + end | |
| 130 | + else | |
| 131 | + render :text => _('The input was not found') | |
| 132 | + end | |
| 73 | 133 | end |
| 74 | - render :partial => 'shared/select_categories', :locals => {:object_name => 'product', :multiple => false}, :layout => false | |
| 75 | 134 | end |
| 76 | 135 | |
| 77 | - def update_subcategories | |
| 78 | - @current_category = ProductCategory.find(params[:id]) if params[:id] | |
| 79 | - @categories = @current_category ? @current_category.children : ProductCategory.top_level_for(environment) | |
| 80 | - render :partial => 'subcategories' | |
| 136 | + def order_inputs | |
| 137 | + @product = @profile.products.find(params[:id]) | |
| 138 | + @product.order_inputs!(params[:input]) if params[:input] | |
| 139 | + render :nothing => true | |
| 81 | 140 | end |
| 82 | - | |
| 83 | - def new_consumption | |
| 84 | - @consumption = @profile.consumptions.build(params[:consumption]) | |
| 141 | + | |
| 142 | + def remove_input | |
| 143 | + @input = @profile.inputs.find(params[:id]) | |
| 144 | + @product = @input.product | |
| 85 | 145 | if request.post? |
| 86 | - if @consumption.save | |
| 87 | - flash[:notice] = _('Raw material succesfully created') | |
| 88 | - redirect_to :action => 'index' | |
| 146 | + if @input.destroy | |
| 147 | + @inputs = @product.inputs | |
| 148 | + render :partial => 'display_inputs' | |
| 89 | 149 | else |
| 90 | - flash[:notice] = _('Could not create the raw material') | |
| 150 | + render :partial => 'shared/dialog_error_messages', :locals => { :object_name => 'input' } | |
| 91 | 151 | end |
| 92 | 152 | end |
| 93 | 153 | end |
| 94 | 154 | |
| 95 | - def destroy_consumption | |
| 96 | - @consumption = @profile.consumptions.find(params[:id]) | |
| 97 | - if @consumption.destroy | |
| 98 | - flash[:notice] = _('Raw material succesfully removed') | |
| 99 | - else | |
| 100 | - flash[:notice] = _('Could not remove the raw material') | |
| 101 | - end | |
| 102 | - redirect_back_or_default :action => 'index' | |
| 103 | - end | |
| 104 | - | |
| 105 | - def edit_consumption | |
| 106 | - @consumption = @profile.consumptions.find(params[:id]) | |
| 107 | - if request.post? | |
| 108 | - if @consumption.update_attributes(params[:consumption]) | |
| 109 | - flash[:notice] = _('Raw material succesfully updated') | |
| 110 | - redirect_back_or_default :action => 'index' | |
| 111 | - else | |
| 112 | - flash[:notice] = _('Could not update the raw material') | |
| 113 | - end | |
| 155 | + def certifiers_for_selection | |
| 156 | + @qualifier = Qualifier.exists?(params[:id]) ? Qualifier.find(params[:id]) : nil | |
| 157 | + render :update do |page| | |
| 158 | + page.replace_html params[:certifier_area], :partial => 'certifiers_for_selection' | |
| 114 | 159 | end |
| 115 | 160 | end |
| 116 | 161 | ... | ... |
app/controllers/my_profile/maps_controller.rb
| ... | ... | @@ -8,7 +8,7 @@ class MapsController < MyProfileController |
| 8 | 8 | begin |
| 9 | 9 | Profile.transaction do |
| 10 | 10 | if profile.update_attributes!(params[:profile_data]) |
| 11 | - flash[:notice] = _('Address was updated successfully!') | |
| 11 | + session[:notice] = _('Address was updated successfully!') | |
| 12 | 12 | redirect_to :action => 'edit_location' |
| 13 | 13 | end |
| 14 | 14 | end | ... | ... |
app/controllers/my_profile/memberships_controller.rb
| ... | ... | @@ -7,32 +7,12 @@ class MembershipsController < MyProfileController |
| 7 | 7 | end |
| 8 | 8 | |
| 9 | 9 | def new_community |
| 10 | - @wizard = params[:wizard].blank? ? false : params[:wizard] | |
| 11 | 10 | @community = Community.new(params[:community]) |
| 12 | 11 | @community.environment = environment |
| 13 | 12 | if request.post? && @community.valid? |
| 14 | 13 | @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community])) |
| 15 | - if @wizard | |
| 16 | - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true | |
| 17 | - return | |
| 18 | - else | |
| 19 | - redirect_to :action => 'index' | |
| 20 | - return | |
| 21 | - end | |
| 22 | - end | |
| 23 | - if @wizard | |
| 24 | - render :layout => 'wizard' | |
| 14 | + redirect_to :action => 'index' | |
| 15 | + return | |
| 25 | 16 | end |
| 26 | 17 | end |
| 27 | - | |
| 28 | - def destroy_community | |
| 29 | - @community = Community.find(params[:id]) | |
| 30 | - if request.post? | |
| 31 | - if @community.destroy | |
| 32 | - flash[:notice] = _('%s was removed.') % @community.short_name | |
| 33 | - redirect_to :action => 'index' | |
| 34 | - end | |
| 35 | - end | |
| 36 | - end | |
| 37 | - | |
| 38 | 18 | end | ... | ... |
app/controllers/my_profile/profile_design_controller.rb
| ... | ... | @@ -5,7 +5,7 @@ class ProfileDesignController < BoxOrganizerController |
| 5 | 5 | protect 'edit_profile_design', :profile |
| 6 | 6 | |
| 7 | 7 | def available_blocks |
| 8 | - blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock ] | |
| 8 | + blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock ] | |
| 9 | 9 | |
| 10 | 10 | # blocks exclusive for organizations |
| 11 | 11 | if profile.has_members? |
| ... | ... | @@ -24,6 +24,7 @@ class ProfileDesignController < BoxOrganizerController |
| 24 | 24 | if profile.enterprise? |
| 25 | 25 | blocks << DisabledEnterpriseMessageBlock |
| 26 | 26 | blocks << HighlightsBlock |
| 27 | + blocks << FeaturedProductsBlock | |
| 27 | 28 | end |
| 28 | 29 | |
| 29 | 30 | # product block exclusive for enterprises in environments that permits it | ... | ... |
app/controllers/my_profile/profile_editor_controller.rb
| 1 | 1 | class ProfileEditorController < MyProfileController |
| 2 | 2 | |
| 3 | - protect 'edit_profile', :profile | |
| 3 | + protect 'edit_profile', :profile, :except => [:destroy_profile] | |
| 4 | + protect 'destroy_profile', :profile, :only => [:destroy_profile] | |
| 4 | 5 | |
| 5 | 6 | def index |
| 6 | 7 | @pending_tasks = profile.all_pending_tasks.select{|i| user.has_permission?(i.permission, profile)} |
| ... | ... | @@ -25,7 +26,7 @@ class ProfileEditorController < MyProfileController |
| 25 | 26 | if profile.identifier.blank? |
| 26 | 27 | profile.identifier = params[:profile] |
| 27 | 28 | end |
| 28 | - flash[:notice] = _('Cannot update profile') | |
| 29 | + session[:notice] = _('Cannot update profile') | |
| 29 | 30 | end |
| 30 | 31 | end |
| 31 | 32 | end |
| ... | ... | @@ -34,7 +35,7 @@ class ProfileEditorController < MyProfileController |
| 34 | 35 | @to_enable = profile |
| 35 | 36 | if request.post? && params[:confirmation] |
| 36 | 37 | unless @to_enable.update_attribute('enabled', true) |
| 37 | - flash[:notice] = _('%s was not enabled.') % @to_enable.name | |
| 38 | + session[:notice] = _('%s was not enabled.') % @to_enable.name | |
| 38 | 39 | end |
| 39 | 40 | redirect_to :action => 'index' |
| 40 | 41 | end |
| ... | ... | @@ -44,7 +45,7 @@ class ProfileEditorController < MyProfileController |
| 44 | 45 | @to_disable = profile |
| 45 | 46 | if request.post? && params[:confirmation] |
| 46 | 47 | unless @to_disable.update_attribute('enabled', false) |
| 47 | - flash[:notice] = _('%s was not disabled.') % @to_disable.name | |
| 48 | + session[:notice] = _('%s was not disabled.') % @to_disable.name | |
| 48 | 49 | end |
| 49 | 50 | redirect_to :action => 'index' |
| 50 | 51 | end |
| ... | ... | @@ -72,4 +73,14 @@ class ProfileEditorController < MyProfileController |
| 72 | 73 | end |
| 73 | 74 | end |
| 74 | 75 | |
| 76 | + def destroy_profile | |
| 77 | + if request.post? | |
| 78 | + if @profile.destroy | |
| 79 | + session[:notice] = _('The profile was deleted.') | |
| 80 | + redirect_to :controller => 'home' | |
| 81 | + else | |
| 82 | + session[:notice] = _('Could not delete profile') | |
| 83 | + end | |
| 84 | + end | |
| 85 | + end | |
| 75 | 86 | end | ... | ... |
app/controllers/my_profile/profile_members_controller.rb
| ... | ... | @@ -10,23 +10,34 @@ class ProfileMembersController < MyProfileController |
| 10 | 10 | def update_roles |
| 11 | 11 | @roles = params[:roles] ? environment.roles.find(params[:roles].select{|r|!r.to_i.zero?}) : [] |
| 12 | 12 | @roles = @roles.select{|r| r.has_kind?('Profile') } |
| 13 | - @person = profile.members.find { |m| m.id == params[:person].to_i } | |
| 14 | - if @person && @person.define_roles(@roles, profile) | |
| 15 | - flash[:notice] = _('Roles successfuly updated') | |
| 13 | + begin | |
| 14 | + @person = profile.members.find(params[:person]) | |
| 15 | + rescue ActiveRecord::RecordNotFound | |
| 16 | + @person = nil | |
| 17 | + end | |
| 18 | + if !params[:confirmation] && @person && @person.is_last_admin_leaving?(profile, @roles) | |
| 19 | + redirect_to :action => :last_admin, :roles => params[:roles], :person => @person | |
| 16 | 20 | else |
| 17 | - flash[:notice] = _('Couldn\'t change the roles') | |
| 21 | + if @person && @person.define_roles(@roles, profile) | |
| 22 | + session[:notice] = _('Roles successfuly updated') | |
| 23 | + else | |
| 24 | + session[:notice] = _('Couldn\'t change the roles') | |
| 25 | + end | |
| 26 | + if params[:confirmation] | |
| 27 | + redirect_to profile.url | |
| 28 | + else | |
| 29 | + redirect_to :action => :index | |
| 30 | + end | |
| 18 | 31 | end |
| 19 | - redirect_to :action => :index | |
| 20 | 32 | end |
| 21 | 33 | |
| 22 | - def change_role | |
| 23 | - @roles = Profile::Roles.organization_member_roles(environment.id) | |
| 24 | - @member = profile.members.find { |m| m.id == params[:id].to_i } | |
| 25 | - if @member | |
| 26 | - @associations = @member.find_roles(@profile) | |
| 27 | - else | |
| 28 | - redirect_to :action => :index | |
| 29 | - end | |
| 34 | + def last_admin | |
| 35 | + @person = params[:person] | |
| 36 | + @roles = params[:roles] || [] | |
| 37 | + @members = profile.members.select {|member| !profile.admins.include?(member)} | |
| 38 | + @title = _('Current admins') | |
| 39 | + @collection = :profile_admins | |
| 40 | + @remove_action = {:action => 'remove_admin'} | |
| 30 | 41 | end |
| 31 | 42 | |
| 32 | 43 | def add_role |
| ... | ... | @@ -44,21 +55,35 @@ class ProfileMembersController < MyProfileController |
| 44 | 55 | def remove_role |
| 45 | 56 | @association = RoleAssignment.find(:all, :conditions => {:id => params[:id], :target_id => profile.id}) |
| 46 | 57 | if @association.destroy |
| 47 | - flash[:notice] = 'Member succefully unassociated' | |
| 58 | + session[:notice] = 'Member succefully unassociated' | |
| 48 | 59 | else |
| 49 | - flash[:notice] = 'Failed to unassociate member' | |
| 60 | + session[:notice] = 'Failed to unassociate member' | |
| 50 | 61 | end |
| 51 | 62 | render :layout => false |
| 52 | 63 | end |
| 53 | 64 | |
| 65 | + def change_role | |
| 66 | + @roles = Profile::Roles.organization_member_roles(environment.id) | |
| 67 | + begin | |
| 68 | + @member = profile.members.find(params[:id]) | |
| 69 | + rescue ActiveRecord::RecordNotFound | |
| 70 | + @member = nil | |
| 71 | + end | |
| 72 | + if @member | |
| 73 | + @associations = @member.find_roles(@profile) | |
| 74 | + else | |
| 75 | + redirect_to :action => :index | |
| 76 | + end | |
| 77 | + end | |
| 78 | + | |
| 54 | 79 | def unassociate |
| 55 | 80 | member = Person.find(params[:id]) |
| 56 | 81 | associations = member.find_roles(profile) |
| 57 | 82 | RoleAssignment.transaction do |
| 58 | 83 | if associations.map(&:destroy) |
| 59 | - flash[:notice] = 'Member succefully unassociated' | |
| 84 | + session[:notice] = _('Member succesfully unassociated') | |
| 60 | 85 | else |
| 61 | - flash[:notice] = 'Failed to unassociate member' | |
| 86 | + session[:notice] = _('Failed to unassociate member') | |
| 62 | 87 | end |
| 63 | 88 | end |
| 64 | 89 | render :layout => false |
| ... | ... | @@ -69,19 +94,61 @@ class ProfileMembersController < MyProfileController |
| 69 | 94 | |
| 70 | 95 | def add_member |
| 71 | 96 | if profile.enterprise? |
| 72 | - member = Person.find_by_identifier(params[:id]) | |
| 97 | + member = Person.find(params[:id]) | |
| 73 | 98 | member.define_roles(Profile::Roles.all_roles(environment), profile) |
| 74 | 99 | end |
| 75 | 100 | render :layout => false |
| 76 | 101 | end |
| 77 | 102 | |
| 103 | + def add_admin | |
| 104 | + @title = _('Current admins') | |
| 105 | + @collection = :profile_admins | |
| 106 | + | |
| 107 | + if profile.community? | |
| 108 | + member = profile.members.find_by_identifier(params[:id]) | |
| 109 | + profile.add_admin(member) | |
| 110 | + end | |
| 111 | + render :layout => false | |
| 112 | + end | |
| 113 | + | |
| 114 | + def remove_admin | |
| 115 | + @title = _('Current admins') | |
| 116 | + @collection = :profile_admins | |
| 117 | + | |
| 118 | + if profile.community? | |
| 119 | + member = profile.members.find_by_identifier(params[:id]) | |
| 120 | + profile.remove_admin(member) | |
| 121 | + end | |
| 122 | + render :layout => false | |
| 123 | + end | |
| 124 | + | |
| 78 | 125 | def find_users |
| 79 | 126 | if !params[:query] || params[:query].length <= 2 |
| 80 | 127 | @users_found = [] |
| 81 | - else | |
| 82 | - @users_found = Person.find_by_contents(params[:query] + '*') | |
| 128 | + elsif params[:scope] == 'all_users' | |
| 129 | + @users_found = Person.find_by_contents(params[:query] + '*').select {|user| !profile.members.include?(user)} | |
| 130 | + @button_alt = _('Add member') | |
| 131 | + @add_action = {:action => 'add_member'} | |
| 132 | + elsif params[:scope] == 'new_admins' | |
| 133 | + @users_found = Person.find_by_contents(params[:query] + '*').select {|user| profile.members.include?(user) && !profile.admins.include?(user)} | |
| 134 | + @button_alt = _('Add member') | |
| 135 | + @add_action = {:action => 'add_admin'} | |
| 83 | 136 | end |
| 84 | 137 | render :layout => false |
| 85 | 138 | end |
| 86 | 139 | |
| 140 | + def send_mail | |
| 141 | + @mailing = profile.mailings.build(params[:mailing]) | |
| 142 | + if request.post? | |
| 143 | + @mailing.locale = locale | |
| 144 | + @mailing.person = user | |
| 145 | + if @mailing.save | |
| 146 | + session[:notice] = _('The e-mails are being sent') | |
| 147 | + redirect_to :action => 'index' | |
| 148 | + else | |
| 149 | + session[:notice] = _('Could not create the e-mail') | |
| 150 | + end | |
| 151 | + end | |
| 152 | + end | |
| 153 | + | |
| 87 | 154 | end | ... | ... |
app/controllers/my_profile/tasks_controller.rb
| ... | ... | @@ -4,26 +4,38 @@ class TasksController < MyProfileController |
| 4 | 4 | |
| 5 | 5 | def index |
| 6 | 6 | @tasks = profile.all_pending_tasks.sort_by(&:created_at) |
| 7 | + @failed = params ? params[:failed] : {} | |
| 7 | 8 | end |
| 8 | 9 | |
| 9 | 10 | def processed |
| 10 | 11 | @tasks = profile.all_finished_tasks.sort_by(&:created_at) |
| 11 | 12 | end |
| 12 | 13 | |
| 13 | - VALID_DECISIONS = [ 'finish', 'cancel' ] | |
| 14 | + VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] | |
| 14 | 15 | |
| 15 | 16 | def close |
| 16 | - decision = params[:decision] | |
| 17 | - if request.post? && VALID_DECISIONS.include?(decision) && params[:id] | |
| 18 | - task = profile.find_in_all_tasks(params[:id]) | |
| 19 | - task.update_attributes!(params[:task]) | |
| 20 | - begin | |
| 21 | - task.send(decision) | |
| 22 | - rescue Exception => ex | |
| 23 | - flash[:notice] = ex.clean_message | |
| 17 | + failed = {} | |
| 18 | + | |
| 19 | + params[:tasks].each do |id, value| | |
| 20 | + decision = value[:decision] | |
| 21 | + if request.post? && VALID_DECISIONS.include?(decision) && id && decision != 'skip' | |
| 22 | + task = profile.find_in_all_tasks(id) | |
| 23 | + task.update_attributes!(value[:task]) | |
| 24 | + begin | |
| 25 | + task.send(decision) | |
| 26 | + rescue Exception => ex | |
| 27 | + message = "#{task.title} (#{task.requestor ? task.requestor.name : task.author_name})" | |
| 28 | + failed[ex.clean_message] ? failed[ex.clean_message] << message : failed[ex.clean_message] = [message] | |
| 29 | + end | |
| 24 | 30 | end |
| 25 | 31 | end |
| 26 | - redirect_to :action => 'index' | |
| 32 | + | |
| 33 | + if failed.blank? | |
| 34 | + session[:notice] = _("All decisions were applied successfully.") | |
| 35 | + else | |
| 36 | + session[:notice] = _("Some decisions couldn't be applied.") | |
| 37 | + end | |
| 38 | + redirect_to params.merge!(:action => 'index', :failed => failed) | |
| 27 | 39 | end |
| 28 | 40 | |
| 29 | 41 | def new | ... | ... |
app/controllers/my_profile/themes_controller.rb
| ... | ... | @@ -8,6 +8,11 @@ class ThemesController < MyProfileController |
| 8 | 8 | redirect_to :action => 'index' |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | + def unset | |
| 12 | + profile.update_theme(nil) | |
| 13 | + redirect_to :action => 'index' | |
| 14 | + end | |
| 15 | + | |
| 11 | 16 | def index |
| 12 | 17 | @themes = profile.environment.themes + Theme.approved_themes(profile) |
| 13 | 18 | @current_theme = profile.theme |
| ... | ... | @@ -18,7 +23,7 @@ class ThemesController < MyProfileController |
| 18 | 23 | |
| 19 | 24 | def new |
| 20 | 25 | if !request.xhr? |
| 21 | - id = params[:name].to_slug | |
| 26 | + id = params[:name] ? params[:name].to_slug : 'my-theme' | |
| 22 | 27 | t = Theme.new(id, :name => params[:name], :owner => profile, :public => false) |
| 23 | 28 | t.save |
| 24 | 29 | redirect_to :action => 'index' | ... | ... |
app/controllers/public/account_controller.rb
| 1 | 1 | class AccountController < ApplicationController |
| 2 | 2 | |
| 3 | + no_design_blocks | |
| 4 | + | |
| 3 | 5 | inverse_captcha :field => 'e_mail' |
| 4 | 6 | |
| 5 | - require_ssl :except => [ :login_popup, :logout_popup, :wizard, :profile_details ] | |
| 7 | + require_ssl :except => [ :login_popup, :logout_popup, :profile_details ] | |
| 6 | 8 | |
| 7 | 9 | before_filter :login_required, :only => [:activation_question, :accept_terms, :activate_enterprise] |
| 8 | 10 | before_filter :redirect_if_logged_in, :only => [:login, :signup] |
| ... | ... | @@ -28,10 +30,10 @@ class AccountController < ApplicationController |
| 28 | 30 | end |
| 29 | 31 | if redirect? |
| 30 | 32 | go_to_initial_page |
| 31 | - flash[:notice] = _("Logged in successfully") | |
| 33 | + session[:notice] = _("Logged in successfully") | |
| 32 | 34 | end |
| 33 | 35 | else |
| 34 | - flash[:notice] = _('Incorrect username or password') if redirect? | |
| 36 | + session[:notice] = _('Incorrect username or password') if redirect? | |
| 35 | 37 | redirect_to :back if redirect? |
| 36 | 38 | end |
| 37 | 39 | end |
| ... | ... | @@ -48,8 +50,6 @@ class AccountController < ApplicationController |
| 48 | 50 | # action to register an user to the application |
| 49 | 51 | def signup |
| 50 | 52 | @invitation_code = params[:invitation_code] |
| 51 | - @wizard = params[:wizard].blank? ? false : params[:wizard] | |
| 52 | - @step = 1 | |
| 53 | 53 | begin |
| 54 | 54 | @user = User.new(params[:user]) |
| 55 | 55 | @user.terms_of_use = environment.terms_of_use |
| ... | ... | @@ -68,38 +68,17 @@ class AccountController < ApplicationController |
| 68 | 68 | invitation.update_attributes!({:friend => @user.person}) |
| 69 | 69 | invitation.finish |
| 70 | 70 | end |
| 71 | - flash[:notice] = _("Thanks for signing up!") | |
| 72 | - if @wizard | |
| 73 | - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true | |
| 74 | - return | |
| 75 | - else | |
| 76 | - go_to_initial_page if redirect? | |
| 77 | - end | |
| 71 | + session[:notice] = _("Thanks for signing up!") | |
| 72 | + go_to_initial_page if redirect? | |
| 78 | 73 | end |
| 79 | - if @wizard | |
| 80 | - render :layout => 'wizard' | |
| 81 | - end | |
| 82 | 74 | rescue ActiveRecord::RecordInvalid |
| 83 | 75 | @person.valid? |
| 84 | 76 | @person.errors.delete(:identifier) |
| 85 | 77 | @person.errors.delete(:user_id) |
| 86 | - if @wizard | |
| 87 | - render :action => 'signup', :layout => 'wizard' | |
| 88 | - else | |
| 89 | - render :action => 'signup' | |
| 90 | - end | |
| 78 | + render :action => 'signup' | |
| 91 | 79 | end |
| 92 | 80 | end |
| 93 | 81 | |
| 94 | - def wizard | |
| 95 | - render :layout => false | |
| 96 | - end | |
| 97 | - | |
| 98 | - def profile_details | |
| 99 | - @profile = Profile.find_by_identifier(params[:profile]) | |
| 100 | - render :partial => 'profile_details', :layout => 'wizard' | |
| 101 | - end | |
| 102 | - | |
| 103 | 82 | # action to perform logout from the application |
| 104 | 83 | def logout |
| 105 | 84 | if logged_in? |
| ... | ... | @@ -107,7 +86,7 @@ class AccountController < ApplicationController |
| 107 | 86 | end |
| 108 | 87 | cookies.delete :auth_token |
| 109 | 88 | reset_session |
| 110 | - flash[:notice] = _("You have been logged out.") | |
| 89 | + session[:notice] = _("You have been logged out.") | |
| 111 | 90 | redirect_to :controller => 'home', :action => 'index' |
| 112 | 91 | end |
| 113 | 92 | |
| ... | ... | @@ -118,10 +97,10 @@ class AccountController < ApplicationController |
| 118 | 97 | @user.change_password!(params[:current_password], |
| 119 | 98 | params[:new_password], |
| 120 | 99 | params[:new_password_confirmation]) |
| 121 | - flash[:notice] = _('Your password has been changed successfully!') | |
| 100 | + session[:notice] = _('Your password has been changed successfully!') | |
| 122 | 101 | redirect_to :action => 'index' |
| 123 | 102 | rescue User::IncorrectPassword => e |
| 124 | - flash[:notice] = _('The supplied current password is incorrect.') | |
| 103 | + session[:notice] = _('The supplied current password is incorrect.') | |
| 125 | 104 | render :action => 'change_password' |
| 126 | 105 | end |
| 127 | 106 | else |
| ... | ... | @@ -233,6 +212,21 @@ class AccountController < ApplicationController |
| 233 | 212 | render :partial => 'identifier_status' |
| 234 | 213 | end |
| 235 | 214 | |
| 215 | + def user_data | |
| 216 | + user_data = | |
| 217 | + if logged_in? | |
| 218 | + current_user.data_hash | |
| 219 | + else | |
| 220 | + { } | |
| 221 | + end | |
| 222 | + if session[:notice] | |
| 223 | + user_data['notice'] = session[:notice] | |
| 224 | + session[:notice] = nil # consume the notice | |
| 225 | + end | |
| 226 | + | |
| 227 | + render :text => user_data.to_json, :layout => false, :content_type => "application/javascript" | |
| 228 | + end | |
| 229 | + | |
| 236 | 230 | protected |
| 237 | 231 | |
| 238 | 232 | def redirect? | ... | ... |
| ... | ... | @@ -0,0 +1,58 @@ |
| 1 | +class BrowseController < PublicController | |
| 2 | + | |
| 3 | + no_design_blocks | |
| 4 | + | |
| 5 | + FILTERS = %w( | |
| 6 | + more_recent | |
| 7 | + more_active | |
| 8 | + more_popular | |
| 9 | + ) | |
| 10 | + | |
| 11 | + def people | |
| 12 | + @filter = filter | |
| 13 | + @title = self.filter_description(params[:action] + '_' + @filter ) | |
| 14 | + | |
| 15 | + @results = @environment.people.visible.send(@filter) | |
| 16 | + | |
| 17 | + if params[:query].blank? | |
| 18 | + @results = @results.paginate(:per_page => 27, :page => params[:page]) | |
| 19 | + else | |
| 20 | + @results = @results.find_by_contents(params[:query]).paginate(:per_page => 27, :page => params[:page]) | |
| 21 | + end | |
| 22 | + end | |
| 23 | + | |
| 24 | + def communities | |
| 25 | + @filter = filter | |
| 26 | + @title = self.filter_description(params[:action] + '_' + @filter ) | |
| 27 | + | |
| 28 | + @results = @environment.communities.visible.send(@filter) | |
| 29 | + | |
| 30 | + if params[:query].blank? | |
| 31 | + @results = @results.paginate(:per_page => 27, :page => params[:page]) | |
| 32 | + else | |
| 33 | + @results = @results.find_by_contents(params[:query]).paginate(:per_page => 27, :page => params[:page]) | |
| 34 | + end | |
| 35 | + end | |
| 36 | + | |
| 37 | + protected | |
| 38 | + | |
| 39 | + def filter | |
| 40 | + if FILTERS.include?(params[:filter]) | |
| 41 | + params[:filter] | |
| 42 | + else | |
| 43 | + 'more_recent' | |
| 44 | + end | |
| 45 | + end | |
| 46 | + | |
| 47 | + def filter_description(str) | |
| 48 | + { | |
| 49 | + 'people_more_recent' => _('More recent people'), | |
| 50 | + 'people_more_active' => _('More active people'), | |
| 51 | + 'people_more_popular' => _('More popular people'), | |
| 52 | + 'communities_more_recent' => _('More recent communities'), | |
| 53 | + 'communities_more_active' => _('More active communities'), | |
| 54 | + 'communities_more_popular' => _('More popular communities'), | |
| 55 | + }[str] || str | |
| 56 | + end | |
| 57 | + | |
| 58 | +end | ... | ... |
app/controllers/public/catalog_controller.rb
| 1 | 1 | class CatalogController < PublicController |
| 2 | 2 | needs_profile |
| 3 | + | |
| 3 | 4 | before_filter :check_enterprise_and_environment |
| 4 | 5 | |
| 5 | 6 | def index |
| 6 | - @products = @profile.products | |
| 7 | + @products = @profile.products.paginate(:per_page => 10, :page => params[:page]) | |
| 7 | 8 | end |
| 8 | 9 | |
| 9 | - def show | |
| 10 | - @product = @profile.products.find(params[:id]) | |
| 11 | - end | |
| 12 | - | |
| 13 | 10 | protected |
| 14 | 11 | def check_enterprise_and_environment |
| 15 | 12 | unless @profile.kind_of?(Enterprise) && !@profile.environment.enabled?('disable_products_for_enterprises') | ... | ... |
| ... | ... | @@ -0,0 +1,52 @@ |
| 1 | +class ChatController < PublicController | |
| 2 | + | |
| 3 | + before_filter :login_required | |
| 4 | + before_filter :check_environment_feature | |
| 5 | + | |
| 6 | + def start_session | |
| 7 | + login = user.jid | |
| 8 | + password = current_user.crypted_password | |
| 9 | + begin | |
| 10 | + jid, sid, rid = RubyBOSH.initialize_session(login, password, "http://#{environment.default_hostname}/http-bind", | |
| 11 | + :wait => 30, :hold => 1, :window => 5) | |
| 12 | + session_data = { :jid => jid, :sid => sid, :rid => rid } | |
| 13 | + render :text => session_data.to_json, :layout => false, :content_type => 'application/javascript' | |
| 14 | + rescue | |
| 15 | + render :action => 'start_session_error', :layout => false, :status => 500 | |
| 16 | + end | |
| 17 | + end | |
| 18 | + | |
| 19 | + def avatar | |
| 20 | + profile = environment.profiles.find_by_identifier(params[:id]) | |
| 21 | + filename, mimetype = profile_icon(profile, :minor, true) | |
| 22 | + data = File.read(File.join(RAILS_ROOT, 'public', filename)) | |
| 23 | + render :text => data, :layout => false, :content_type => mimetype | |
| 24 | + expires_in 24.hours | |
| 25 | + end | |
| 26 | + | |
| 27 | + def index | |
| 28 | + presence = current_user.last_chat_status | |
| 29 | + if presence.blank? or presence == 'chat' | |
| 30 | + render :action => 'auto_connect_online' | |
| 31 | + else | |
| 32 | + render :action => 'auto_connect_busy' | |
| 33 | + end | |
| 34 | + end | |
| 35 | + | |
| 36 | + def update_presence_status | |
| 37 | + if request.xhr? | |
| 38 | + current_user.update_attributes({:chat_status_at => DateTime.now}.merge(params[:status] || {})) | |
| 39 | + end | |
| 40 | + render :nothing => true | |
| 41 | + end | |
| 42 | + | |
| 43 | + protected | |
| 44 | + | |
| 45 | + def check_environment_feature | |
| 46 | + unless environment.enabled?('xmpp_chat') | |
| 47 | + render_not_found | |
| 48 | + return | |
| 49 | + end | |
| 50 | + end | |
| 51 | + | |
| 52 | +end | ... | ... |
app/controllers/public/contact_controller.rb
| ... | ... | @@ -12,10 +12,10 @@ class ContactController < PublicController |
| 12 | 12 | @contact.city = (!params[:city].blank? && City.exists?(params[:city])) ? City.find(params[:city]).name : nil |
| 13 | 13 | @contact.state = (!params[:state].blank? && State.exists?(params[:state])) ? State.find(params[:state]).name : nil |
| 14 | 14 | if @contact.deliver |
| 15 | - flash[:notice] = _('Contact successfully sent') | |
| 15 | + session[:notice] = _('Contact successfully sent') | |
| 16 | 16 | redirect_to :action => 'new' |
| 17 | 17 | else |
| 18 | - flash[:notice] = _('Contact not sent') | |
| 18 | + session[:notice] = _('Contact not sent') | |
| 19 | 19 | end |
| 20 | 20 | else |
| 21 | 21 | @contact = user.build_contact(profile) | ... | ... |
app/controllers/public/content_viewer_controller.rb
| ... | ... | @@ -51,6 +51,8 @@ class ContentViewerController < ApplicationController |
| 51 | 51 | return |
| 52 | 52 | end |
| 53 | 53 | |
| 54 | + redirect_to_translation | |
| 55 | + | |
| 54 | 56 | # At this point the page will be showed |
| 55 | 57 | @page.hit |
| 56 | 58 | |
| ... | ... | @@ -67,9 +69,6 @@ class ContentViewerController < ApplicationController |
| 67 | 69 | return |
| 68 | 70 | end |
| 69 | 71 | |
| 70 | - # store location if the page is not a download | |
| 71 | - store_location | |
| 72 | - | |
| 73 | 72 | @form_div = params[:form] |
| 74 | 73 | |
| 75 | 74 | if request.post? && params[:comment] && params[self.icaptcha_field].blank? && params[:confirm] == 'true' && @page.accept_comments? |
| ... | ... | @@ -80,22 +79,28 @@ class ContentViewerController < ApplicationController |
| 80 | 79 | remove_comment |
| 81 | 80 | end |
| 82 | 81 | |
| 83 | - if @page.blog? | |
| 82 | + if @page.has_posts? | |
| 84 | 83 | posts = if params[:year] and params[:month] |
| 85 | 84 | filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01") |
| 86 | - @page.posts.by_range(filter_date..Article.last_day_of_month(filter_date)) | |
| 85 | + @page.posts.by_range(filter_date..filter_date.at_end_of_month) | |
| 87 | 86 | else |
| 88 | 87 | @page.posts |
| 89 | 88 | end |
| 90 | - @posts = available_articles(posts, user).paginate :page => params[:npage], :per_page => @page.posts_per_page | |
| 89 | + | |
| 90 | + posts = posts.native_translations if @page.blog? && @page.display_posts_in_current_language? | |
| 91 | + | |
| 92 | + @posts = posts.paginate({ :page => params[:npage], :per_page => @page.posts_per_page }.merge(Article.display_filter(user, profile))) | |
| 93 | + | |
| 94 | + @posts.map!{ |p| p.get_translation_to(FastGettext.locale) } if @page.blog? && @page.display_posts_in_current_language? | |
| 91 | 95 | end |
| 92 | 96 | |
| 93 | - if @page.folder? && @page.view_as == 'image_gallery' | |
| 97 | + if @page.folder? && @page.gallery? | |
| 94 | 98 | @images = @page.images |
| 95 | 99 | @images = @images.paginate(:per_page => per_page, :page => params[:npage]) unless params[:slideshow] |
| 96 | 100 | end |
| 97 | 101 | |
| 98 | - @comments = @page.comments(true) | |
| 102 | + @comments = @page.comments(true).as_thread | |
| 103 | + @comments_count = @page.comments.count | |
| 99 | 104 | if params[:slideshow] |
| 100 | 105 | render :action => 'slideshow', :layout => 'slideshow' |
| 101 | 106 | end |
| ... | ... | @@ -110,8 +115,9 @@ class ContentViewerController < ApplicationController |
| 110 | 115 | if @comment.save |
| 111 | 116 | @page.touch |
| 112 | 117 | @comment = nil # clear the comment form |
| 118 | + redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] | |
| 113 | 119 | else |
| 114 | - @form_div = 'opened' | |
| 120 | + @form_div = 'opened' if params[:comment][:reply_of_id].blank? | |
| 115 | 121 | end |
| 116 | 122 | end |
| 117 | 123 | |
| ... | ... | @@ -119,7 +125,7 @@ class ContentViewerController < ApplicationController |
| 119 | 125 | @comment = @page.comments.find(params[:remove_comment]) |
| 120 | 126 | if (user == @comment.author || user == @page.profile || user.has_permission?(:moderate_comments, @page.profile)) |
| 121 | 127 | @comment.destroy |
| 122 | - flash[:notice] = _('Comment succesfully deleted') | |
| 128 | + session[:notice] = _('Comment succesfully deleted') | |
| 123 | 129 | end |
| 124 | 130 | redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] |
| 125 | 131 | end |
| ... | ... | @@ -127,4 +133,24 @@ class ContentViewerController < ApplicationController |
| 127 | 133 | def per_page |
| 128 | 134 | 12 |
| 129 | 135 | end |
| 136 | + | |
| 137 | + def redirect_to_translation | |
| 138 | + locale = FastGettext.locale | |
| 139 | + if !@page.language.nil? && @page.language != locale | |
| 140 | + translations = [@page.native_translation] + @page.native_translation.translations | |
| 141 | + urls = translations.map{ |t| URI.parse(url_for(t.url)).path } | |
| 142 | + urls << URI.parse(url_for(profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }))).path | |
| 143 | + urls << URI.parse(url_for(profile.admin_url.merge(:controller => 'cms', :action => 'new'))).path | |
| 144 | + referer = URI.parse(url_for(request.referer)).path unless request.referer.blank? | |
| 145 | + unless urls.include?(referer) | |
| 146 | + translations.each do |translation| | |
| 147 | + if translation.language == locale | |
| 148 | + @page = translation | |
| 149 | + redirect_to :profile => @page.profile.identifier, :page => @page.explode_path | |
| 150 | + end | |
| 151 | + end | |
| 152 | + end | |
| 153 | + end | |
| 154 | + end | |
| 155 | + | |
| 130 | 156 | end | ... | ... |
app/controllers/public/enterprise_registration_controller.rb
| ... | ... | @@ -16,9 +16,9 @@ class EnterpriseRegistrationController < ApplicationController |
| 16 | 16 | @create_enterprise.target = Profile.find(params[:create_enterprise][:target_id]) |
| 17 | 17 | end |
| 18 | 18 | elsif @validation == :admin || @validation == :none |
| 19 | - @create_enterprise.target = @create_enterprise.environment | |
| 19 | + @create_enterprise.target = environment | |
| 20 | 20 | end |
| 21 | - @create_enterprise.requestor = current_user.person | |
| 21 | + @create_enterprise.requestor = user | |
| 22 | 22 | the_action = |
| 23 | 23 | if request.post? |
| 24 | 24 | if @create_enterprise.valid_before_selecting_target? | ... | ... |
app/controllers/public/events_controller.rb
| ... | ... | @@ -13,7 +13,7 @@ class EventsController < PublicController |
| 13 | 13 | @events_of_the_day = profile.events.by_day(@selected_day) |
| 14 | 14 | end |
| 15 | 15 | |
| 16 | - events = profile.events.by_range(Event.first_day_of_month(date - 1.month)..Event.last_day_of_month(date + 1.month)) | |
| 16 | + events = profile.events.by_range((date - 1.month).at_beginning_of_month..(date + 1.month).at_end_of_month) | |
| 17 | 17 | |
| 18 | 18 | @calendar = populate_calendar(date, events) |
| 19 | 19 | @previous_calendar = populate_calendar(date - 1.month, events) | ... | ... |
app/controllers/public/home_controller.rb
| ... | ... | @@ -7,8 +7,8 @@ class HomeController < PublicController |
| 7 | 7 | @news_cache_key = environment.portal_news_cache_key |
| 8 | 8 | if !read_fragment(@news_cache_key) |
| 9 | 9 | portal_community = environment.portal_community |
| 10 | - @portal_news = portal_community.news(5) | |
| 11 | 10 | @highlighted_news = portal_community.news(2, true) |
| 11 | + @portal_news = portal_community.news(7, true) - @highlighted_news | |
| 12 | 12 | @area_news = environment.portal_folders |
| 13 | 13 | end |
| 14 | 14 | end | ... | ... |
app/controllers/public/invite_controller.rb
| ... | ... | @@ -2,40 +2,57 @@ class InviteController < PublicController |
| 2 | 2 | |
| 3 | 3 | needs_profile |
| 4 | 4 | before_filter :login_required |
| 5 | - before_filter :check_permissions_to_invite, :only => 'friends' | |
| 5 | + before_filter :check_permissions_to_invite | |
| 6 | 6 | |
| 7 | - def friends | |
| 8 | - step = params[:step] | |
| 7 | + def select_address_book | |
| 8 | + @import_from = params[:import_from] || "manual" | |
| 9 | 9 | if request.post? |
| 10 | - if step == '1' | |
| 11 | - begin | |
| 12 | - @contacts = Invitation.get_contacts(params[:import_from], params[:login], params[:password]) | |
| 13 | - rescue | |
| 14 | - @login = params[:login] | |
| 15 | - flash.now[:notice] = _('There was an error while looking for your contact list. Did you enter correct login and password?') | |
| 16 | - end | |
| 17 | - elsif step == '2' | |
| 18 | - manual_import_addresses = params[:manual_import_addresses] | |
| 19 | - webmail_import_addresses = params[:webmail_import_addresses] | |
| 20 | - contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses) | |
| 21 | - if !params[:mail_template].match(/<url>/) | |
| 22 | - flash.now[:notice] = _('<url> is needed in invitation mail.') | |
| 23 | - elsif !contacts_to_invite.empty? | |
| 24 | - Invitation.invite(current_user.person, contacts_to_invite, params[:mail_template], profile) | |
| 25 | - flash[:notice] = _('Your invitations have been sent.') | |
| 26 | - redirect_back_or_default :controller => 'profile' | |
| 10 | + contact_list = ContactList.create | |
| 11 | + Delayed::Job.enqueue GetEmailContactsJob.new(@import_from, params[:login], params[:password], contact_list.id) if @import_from != 'manual' | |
| 12 | + redirect_to :action => 'select_friends', :contact_list => contact_list.id, :import_from => @import_from | |
| 13 | + end | |
| 14 | + end | |
| 15 | + | |
| 16 | + def select_friends | |
| 17 | + @contact_list = ContactList.find(params[:contact_list]) | |
| 18 | + @mail_template = params[:mail_template] || environment.invitation_mail_template(profile) | |
| 19 | + @import_from = params[:import_from] || "manual" | |
| 20 | + if request.post? | |
| 21 | + manual_import_addresses = params[:manual_import_addresses] | |
| 22 | + webmail_import_addresses = params[:webmail_import_addresses] | |
| 23 | + contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses) | |
| 24 | + if !contacts_to_invite.empty? | |
| 25 | + Delayed::Job.enqueue InvitationJob.new(current_user.person.id, contacts_to_invite, params[:mail_template], profile.id, @contact_list.id, locale) | |
| 26 | + session[:notice] = _('Your invitations are being sent.') | |
| 27 | + if profile.person? | |
| 28 | + redirect_to :controller => 'profile', :action => 'friends' | |
| 27 | 29 | else |
| 28 | - flash.now[:notice] = _('Please enter a valid email address.') | |
| 30 | + redirect_to :controller => 'profile', :action => 'members' | |
| 29 | 31 | end |
| 30 | - @contacts = params[:webmail_friends] ? params[:webmail_friends].map {|e| YAML.load(e)} : [] | |
| 31 | - @manual_import_addresses = manual_import_addresses || "" | |
| 32 | - @webmail_import_addresses = webmail_import_addresses || [] | |
| 32 | + return | |
| 33 | + else | |
| 34 | + session[:notice] = _('Please enter a valid email address.') | |
| 33 | 35 | end |
| 34 | - else | |
| 35 | - store_location(request.referer) | |
| 36 | + @manual_import_addresses = manual_import_addresses || "" | |
| 37 | + @webmail_import_addresses = webmail_import_addresses || [] | |
| 36 | 38 | end |
| 37 | - @import_from = params[:import_from] || "manual" | |
| 38 | - @mail_template = params[:mail_template] || environment.invitation_mail_template(profile) | |
| 39 | + end | |
| 40 | + | |
| 41 | + def invitation_data | |
| 42 | + contact_list = ContactList.find(params[:contact_list]) | |
| 43 | + render :text => contact_list.data.to_json, :layout => false, :content_type => "application/javascript" | |
| 44 | + end | |
| 45 | + | |
| 46 | + def add_contact_list | |
| 47 | + contact_list = ContactList.find(params[:contact_list]) | |
| 48 | + contacts = contact_list.list | |
| 49 | + render :partial => 'invite/contact_list', :locals => {:contacts => contacts} | |
| 50 | + end | |
| 51 | + | |
| 52 | + def cancel_fetching_emails | |
| 53 | + contact_list = ContactList.find(params[:contact_list]) | |
| 54 | + contact_list.destroy | |
| 55 | + redirect_to :action => 'select_address_book' | |
| 39 | 56 | end |
| 40 | 57 | |
| 41 | 58 | protected | ... | ... |
app/controllers/public/profile_controller.rb
| 1 | 1 | class ProfileController < PublicController |
| 2 | 2 | |
| 3 | 3 | needs_profile |
| 4 | - before_filter :check_access_to_profile, :except => [:join, :refuse_join, :refuse_for_now, :index] | |
| 5 | - before_filter :store_before_join, :only => [:join] | |
| 6 | - before_filter :login_required, :only => [:join, :refuse_join, :leave, :unblock] | |
| 4 | + before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add] | |
| 5 | + before_filter :store_before_join, :only => [:join, :join_not_logged] | |
| 6 | + before_filter :login_required, :only => [:add, :join, :join_not_logged, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_scraps, :view_more_activities, :view_more_network_activities] | |
| 7 | 7 | |
| 8 | 8 | helper TagsHelper |
| 9 | 9 | |
| 10 | 10 | def index |
| 11 | + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page]) | |
| 12 | + @wall_items = [] | |
| 13 | + @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) : [] | |
| 14 | + if logged_in? && current_person.follows?(@profile) | |
| 15 | + @network_activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) if @network_activities.empty? | |
| 16 | + @wall_items = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) | |
| 17 | + end | |
| 11 | 18 | @tags = profile.article_tags |
| 12 | 19 | unless profile.display_info_to?(user) |
| 13 | 20 | profile.visible? ? private_profile : invisible_profile |
| ... | ... | @@ -73,20 +80,27 @@ class ProfileController < PublicController |
| 73 | 80 | end |
| 74 | 81 | |
| 75 | 82 | def join |
| 76 | - @wizard = params[:wizard] | |
| 77 | - if request.post? && params[:confirmation] | |
| 78 | - profile.add_member(current_user.person) | |
| 79 | - flash[:notice] = _('%s administrator still needs to accept you as member.') % profile.name if profile.closed? | |
| 80 | - if @wizard | |
| 81 | - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true | |
| 83 | + if !user.memberships.include?(profile) | |
| 84 | + profile.add_member(user) | |
| 85 | + if !profile.members.include?(user) | |
| 86 | + render :text => {:message => _('%s administrator still needs to accept you as member.') % profile.name}.to_json | |
| 82 | 87 | else |
| 83 | - redirect_to_before_join | |
| 88 | + render :text => {:message => _('You just became a member of %s.') % profile.name}.to_json | |
| 84 | 89 | end |
| 85 | 90 | else |
| 86 | - store_location(request.referer) | |
| 87 | - if current_user.person.memberships.include?(profile) | |
| 88 | - flash[:notice] = _('You are already a member of "%s"') % profile.name | |
| 89 | - redirect_back_or_default profile.url | |
| 91 | + render :text => {:message => _('You are already a member of %s.') % profile.name}.to_json | |
| 92 | + end | |
| 93 | + end | |
| 94 | + | |
| 95 | + def join_not_logged | |
| 96 | + if request.post? | |
| 97 | + profile.add_member(user) | |
| 98 | + session[:notice] = _('%s administrator still needs to accept you as member.') % profile.name if profile.closed? | |
| 99 | + redirect_to_before_join | |
| 100 | + else | |
| 101 | + if user.memberships.include?(profile) | |
| 102 | + session[:notice] = _('You are already a member of %s.') % profile.name | |
| 103 | + redirect_to profile.url | |
| 90 | 104 | return |
| 91 | 105 | end |
| 92 | 106 | if request.xhr? |
| ... | ... | @@ -96,47 +110,109 @@ class ProfileController < PublicController |
| 96 | 110 | end |
| 97 | 111 | |
| 98 | 112 | def leave |
| 99 | - @wizard = params[:wizard] | |
| 100 | - if request.post? && params[:confirmation] | |
| 101 | - profile.remove_member(current_user.person) | |
| 102 | - if @wizard | |
| 103 | - redirect_to :controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true | |
| 113 | + if current_person.memberships.include?(profile) | |
| 114 | + if current_person.is_last_admin?(profile) | |
| 115 | + render :text => {:redirect_to => url_for({:controller => 'profile_members', :action => 'last_admin', :person => current_person.id})}.to_json | |
| 104 | 116 | else |
| 105 | - redirect_back_or_default profile.url | |
| 117 | + render :text => current_person.leave(profile, params[:reload]) | |
| 106 | 118 | end |
| 107 | 119 | else |
| 108 | - store_location(request.referer) | |
| 109 | - if request.xhr? | |
| 110 | - render :layout => false | |
| 111 | - end | |
| 120 | + render :text => {:message => _('You are not a member of %s.') % profile.name}.to_json | |
| 112 | 121 | end |
| 113 | 122 | end |
| 114 | 123 | |
| 115 | - def refuse_join | |
| 116 | - p = current_user.person | |
| 117 | - p.refused_communities << profile | |
| 118 | - p.save | |
| 119 | - redirect_to profile.url | |
| 124 | + def check_membership | |
| 125 | + unless logged_in? | |
| 126 | + render :text => '' | |
| 127 | + return | |
| 128 | + end | |
| 129 | + if user.memberships.include?(profile) | |
| 130 | + render :text => 'true' | |
| 131 | + else | |
| 132 | + render :text => 'false' | |
| 133 | + end | |
| 120 | 134 | end |
| 121 | 135 | |
| 122 | - def refuse_for_now | |
| 123 | - session[:no_asking] ||= [] | |
| 124 | - session[:no_asking].shift if session[:no_asking].size >= 10 | |
| 125 | - session[:no_asking] << profile.id | |
| 126 | - render :text => '', :layout => false | |
| 136 | + def add | |
| 137 | + # FIXME this shouldn't be in Person model? | |
| 138 | + if !user.memberships.include?(profile) | |
| 139 | + AddFriend.create!(:person => user, :friend => profile) | |
| 140 | + render :text => _('%s still needs to accept being your friend.') % profile.name | |
| 141 | + else | |
| 142 | + render :text => _('You are already a friend of %s.') % profile.name | |
| 143 | + end | |
| 144 | + end | |
| 145 | + | |
| 146 | + def check_friendship | |
| 147 | + unless logged_in? | |
| 148 | + render :text => '' | |
| 149 | + return | |
| 150 | + end | |
| 151 | + if user == profile || user.already_request_friendship?(profile) || user.is_a_friend?(profile) | |
| 152 | + render :text => 'true' | |
| 153 | + else | |
| 154 | + render :text => 'false' | |
| 155 | + end | |
| 127 | 156 | end |
| 128 | 157 | |
| 129 | 158 | def unblock |
| 130 | 159 | if current_user.person.is_admin?(profile.environment) |
| 131 | 160 | profile.unblock |
| 132 | - flash[:notice] = _("You have unblocked %s successfully. ") % profile.name | |
| 161 | + session[:notice] = _("You have unblocked %s successfully. ") % profile.name | |
| 133 | 162 | redirect_to :controller => 'profile', :action => 'index' |
| 134 | 163 | else |
| 135 | - message = _('You are not allowed to unblock enterprises in this environment.') | |
| 164 | + message = __('You are not allowed to unblock enterprises in this environment.') | |
| 136 | 165 | render_access_denied(message) |
| 137 | 166 | end |
| 138 | 167 | end |
| 139 | 168 | |
| 169 | + def leave_scrap | |
| 170 | + sender = params[:sender_id].nil? ? current_user.person : Person.find(params[:sender_id]) | |
| 171 | + receiver = params[:receiver_id].nil? ? @profile : Person.find(params[:receiver_id]) | |
| 172 | + @scrap = Scrap.new(params[:scrap]) | |
| 173 | + @scrap.sender= sender | |
| 174 | + @scrap.receiver= receiver | |
| 175 | + @tab_action = params[:tab_action] | |
| 176 | + @message = @scrap.save ? _("Message successfully sent.") : _("You can't leave an empty message.") | |
| 177 | + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) if params[:not_load_scraps].nil? | |
| 178 | + render :partial => 'leave_scrap' | |
| 179 | + end | |
| 180 | + | |
| 181 | + def view_more_scraps | |
| 182 | + @scraps = @profile.scraps_received.not_replies.paginate(:per_page => 30, :page => params[:page]) | |
| 183 | + render :partial => 'profile_scraps', :locals => {:scraps => @scraps} | |
| 184 | + end | |
| 185 | + | |
| 186 | + def view_more_activities | |
| 187 | + @activities = @profile.tracked_actions.paginate(:per_page => 30, :page => params[:page]) | |
| 188 | + render :partial => 'profile_activities', :locals => {:activities => @activities} | |
| 189 | + end | |
| 190 | + | |
| 191 | + def view_more_network_activities | |
| 192 | + @activities = @profile.tracked_notifications.paginate(:per_page => 30, :page => params[:page]) | |
| 193 | + render :partial => 'profile_network_activities', :locals => {:network_activities => @activities} | |
| 194 | + end | |
| 195 | + | |
| 196 | + def remove_scrap | |
| 197 | + begin | |
| 198 | + scrap = current_user.person.scraps(params[:scrap_id]) | |
| 199 | + scrap.destroy | |
| 200 | + render :text => _('Scrap successfully removed.') | |
| 201 | + rescue | |
| 202 | + render :text => _('You could not remove this scrap') | |
| 203 | + end | |
| 204 | + end | |
| 205 | + | |
| 206 | + def remove_activity | |
| 207 | + begin | |
| 208 | + activity = current_person.tracked_actions.find(params[:activity_id]) | |
| 209 | + activity.destroy | |
| 210 | + render :text => _('Activity successfully removed.') | |
| 211 | + rescue | |
| 212 | + render :text => _('You could not remove this activity') | |
| 213 | + end | |
| 214 | + end | |
| 215 | + | |
| 140 | 216 | protected |
| 141 | 217 | |
| 142 | 218 | def check_access_to_profile |
| ... | ... | @@ -146,7 +222,9 @@ class ProfileController < PublicController |
| 146 | 222 | end |
| 147 | 223 | |
| 148 | 224 | def store_before_join |
| 149 | - session[:before_join] = request.referer unless logged_in? | |
| 225 | + if session[:before_join].nil? | |
| 226 | + session[:before_join] = request.referer | |
| 227 | + end | |
| 150 | 228 | end |
| 151 | 229 | |
| 152 | 230 | def redirect_to_before_join |
| ... | ... | @@ -155,14 +233,14 @@ class ProfileController < PublicController |
| 155 | 233 | session[:before_join] = nil |
| 156 | 234 | redirect_to back |
| 157 | 235 | else |
| 158 | - redirect_back_or_default profile.url | |
| 236 | + redirect_to profile.url | |
| 159 | 237 | end |
| 160 | 238 | end |
| 161 | 239 | |
| 162 | 240 | def private_profile |
| 163 | 241 | if profile.person? |
| 164 | 242 | @action = :add_friend |
| 165 | - @message = _("The content here is available to %s's friends only." % profile.short_name) | |
| 243 | + @message = _("The content here is available to %s's friends only.") % profile.short_name | |
| 166 | 244 | else |
| 167 | 245 | @action = :join |
| 168 | 246 | @message = _('The contents in this community is available to members only.') | ... | ... |
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | +class ProfileSearchController < PublicController | |
| 2 | + | |
| 3 | + include SearchHelper | |
| 4 | + | |
| 5 | + needs_profile | |
| 6 | + before_filter :check_access_to_profile | |
| 7 | + | |
| 8 | + def index | |
| 9 | + @q = params[:q] | |
| 10 | + unless @q.blank? | |
| 11 | + @filtered_query = remove_stop_words(@q) | |
| 12 | + if params[:where] == 'environment' | |
| 13 | + redirect_to :controller => 'search', :query => @q | |
| 14 | + else | |
| 15 | + @results = profile.articles.published.find_by_contents(@filtered_query).paginate(:per_page => 10, :page => params[:page]) | |
| 16 | + end | |
| 17 | + end | |
| 18 | + end | |
| 19 | + | |
| 20 | + protected | |
| 21 | + | |
| 22 | + def check_access_to_profile | |
| 23 | + unless profile.display_info_to?(user) | |
| 24 | + redirect_to :controller => 'profile', :action => 'index' | |
| 25 | + end | |
| 26 | + end | |
| 27 | + | |
| 28 | +end | ... | ... |
app/controllers/public/search_controller.rb
| ... | ... | @@ -102,7 +102,7 @@ class SearchController < PublicController |
| 102 | 102 | |
| 103 | 103 | if month || year |
| 104 | 104 | date = Date.new(year.to_i, month.to_i, 1) |
| 105 | - result[:date_range] = (date - 1.month)..Event.last_day_of_month(date + 1.month) | |
| 105 | + result[:date_range] = (date - 1.month)..(date + 1.month).at_end_of_month | |
| 106 | 106 | end |
| 107 | 107 | |
| 108 | 108 | result |
| ... | ... | @@ -148,8 +148,6 @@ class SearchController < PublicController |
| 148 | 148 | end |
| 149 | 149 | |
| 150 | 150 | def index |
| 151 | - @wizard = params[:wizard].blank? ? false : params[:wizard] | |
| 152 | - @step = 2 | |
| 153 | 151 | @query = params[:query] || '' |
| 154 | 152 | @filtered_query = remove_stop_words(@query) |
| 155 | 153 | @product_category = ProductCategory.find(params[:product_category]) if params[:product_category] |
| ... | ... | @@ -174,20 +172,12 @@ class SearchController < PublicController |
| 174 | 172 | if respond_to?(specific_action) |
| 175 | 173 | @asset_name = getterm(@names[@results.keys.first]) |
| 176 | 174 | send(specific_action) |
| 177 | - if @wizard | |
| 178 | - render :action => specific_action, :layout => 'wizard' | |
| 179 | - else | |
| 180 | - render :action => specific_action | |
| 181 | - end | |
| 175 | + render :action => specific_action | |
| 182 | 176 | return |
| 183 | 177 | end |
| 184 | 178 | end |
| 185 | 179 | |
| 186 | - if @wizard | |
| 187 | - render :action => 'index', :layout => 'wizard' | |
| 188 | - else | |
| 189 | - render :action => 'index' | |
| 190 | - end | |
| 180 | + render :action => 'index' | |
| 191 | 181 | end |
| 192 | 182 | |
| 193 | 183 | alias :assets :index |
| ... | ... | @@ -223,10 +213,10 @@ class SearchController < PublicController |
| 223 | 213 | end |
| 224 | 214 | |
| 225 | 215 | def tag |
| 226 | - @tag = environment.tags.find_by_name(params[:tag]) | |
| 216 | + @tag = params[:tag] | |
| 227 | 217 | @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_env_#{environment.id.to_s}_page_#{params[:npage]}" |
| 228 | 218 | if is_cache_expired?(@tag_cache_key, true) |
| 229 | - @tagged = environment.articles.find_tagged_with(@tag.name).paginate(:per_page => 10, :page => params[:npage]) | |
| 219 | + @tagged = environment.articles.find_tagged_with(@tag).paginate(:per_page => 10, :page => params[:npage]) | |
| 230 | 220 | end |
| 231 | 221 | end |
| 232 | 222 | ... | ... |
app/helpers/account_helper.rb
| 1 | 1 | module AccountHelper |
| 2 | 2 | |
| 3 | - | |
| 4 | - def button_to_step(type, step, current_step, html_options = {}) | |
| 5 | - if current_step == step | |
| 6 | - the_class = 'active' | |
| 7 | - if html_options.has_key?(:class) | |
| 8 | - html_options[:class] << " #{the_class}" | |
| 9 | - else | |
| 10 | - html_options[:class] = the_class | |
| 11 | - end | |
| 12 | - end | |
| 13 | - if step == 1 | |
| 14 | - url = '#' | |
| 15 | - else | |
| 16 | - url = send('url_step_' + step.to_s) | |
| 17 | - end | |
| 18 | - button(type, step.to_s, url, html_options) | |
| 19 | - end | |
| 20 | - | |
| 21 | - def button_to_step_without_text(type, step, html_options = {}) | |
| 22 | - url = 'url_step_' + step | |
| 23 | - button_without_text(type, step, send(url), html_options) | |
| 24 | - end | |
| 25 | - | |
| 26 | - def button_to_previous_step(step, html_options = {}) | |
| 27 | - step = step - 1 | |
| 28 | - if step > 1 | |
| 29 | - button_to_step_without_text(:left, step.to_s, html_options) | |
| 30 | - end | |
| 31 | - end | |
| 32 | - | |
| 33 | - def button_to_next_step(step, html_options = {}) | |
| 34 | - step = step + 1 | |
| 35 | - if step < 4 | |
| 36 | - button_to_step_without_text(:forward, step.to_s, html_options) | |
| 37 | - end | |
| 38 | - end | |
| 39 | - | |
| 40 | - def url_step_1 | |
| 41 | - options = {:controller => 'account', :action => 'signup', :wizard => true} | |
| 42 | - Noosfero.url_options.merge(options) | |
| 43 | - end | |
| 44 | - | |
| 45 | - def url_step_2 | |
| 46 | - options = {:controller => 'search', :action => 'assets', :asset => 'communities', :wizard => true} | |
| 47 | - Noosfero.url_options.merge(options) | |
| 48 | - end | |
| 49 | - | |
| 50 | - def url_step_3 | |
| 51 | - options = {:controller => 'friends', :action => 'invite', :profile => user.identifier, :wizard => true} | |
| 52 | - Noosfero.url_options.merge(options) | |
| 53 | - end | |
| 54 | - | |
| 55 | 3 | end | ... | ... |
app/helpers/application_helper.rb
| ... | ... | @@ -27,18 +27,7 @@ module ApplicationHelper |
| 27 | 27 | include AccountHelper |
| 28 | 28 | |
| 29 | 29 | def locale |
| 30 | - FastGettext.locale | |
| 31 | - end | |
| 32 | - | |
| 33 | - def load_web2_conf | |
| 34 | - if File.exists?( RAILS_ROOT + '/config/web2.0.yml') | |
| 35 | - YAML.load_file( RAILS_ROOT + '/config/web2.0.yml' ) | |
| 36 | - else | |
| 37 | - {} | |
| 38 | - end | |
| 39 | - end | |
| 40 | - def web2_conf | |
| 41 | - @web_conf ||= load_web2_conf | |
| 30 | + (@page && !@page.language.blank?) ? @page.language : FastGettext.locale | |
| 42 | 31 | end |
| 43 | 32 | |
| 44 | 33 | # Displays context help. You can pass the content of the help message as the |
| ... | ... | @@ -217,7 +206,8 @@ module ApplicationHelper |
| 217 | 206 | if html_options.has_key?(:class) |
| 218 | 207 | the_class << ' ' << html_options[:class] |
| 219 | 208 | end |
| 220 | - link_to(' '+content_tag('span', label), url, html_options.merge(:class => the_class, :title => label)) | |
| 209 | + the_title = html_options[:title] || label | |
| 210 | + link_to(' '+content_tag('span', label), url, html_options.merge(:class => the_class, :title => the_title)) | |
| 221 | 211 | end |
| 222 | 212 | |
| 223 | 213 | def button_to_function(type, label, js_code, html_options = {}, &block) |
| ... | ... | @@ -278,6 +268,19 @@ module ApplicationHelper |
| 278 | 268 | end |
| 279 | 269 | end |
| 280 | 270 | |
| 271 | + def partial_for_task_class(klass, action) | |
| 272 | + if klass.nil? | |
| 273 | + raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' | |
| 274 | + end | |
| 275 | + | |
| 276 | + name = "#{klass.name.underscore}_#{action.to_s}" | |
| 277 | + if File.exists?(File.join(RAILS_ROOT, 'app', 'views', params[:controller], "_#{name}.rhtml")) | |
| 278 | + name | |
| 279 | + else | |
| 280 | + partial_for_task_class(klass.superclass, action) | |
| 281 | + end | |
| 282 | + end | |
| 283 | + | |
| 281 | 284 | def user |
| 282 | 285 | @controller.send(:user) |
| 283 | 286 | end |
| ... | ... | @@ -339,7 +342,7 @@ module ApplicationHelper |
| 339 | 342 | if ENV['RAILS_ENV'] == 'development' && environment.theme == 'random' |
| 340 | 343 | @random_theme ||= Dir.glob('public/designs/themes/*').map { |f| File.basename(f) }.rand |
| 341 | 344 | @random_theme |
| 342 | - elsif ENV['RAILS_ENV'] == 'development' && params[:theme] | |
| 345 | + elsif ENV['RAILS_ENV'] == 'development' && params[:theme] && File.exists?(File.join(Rails.root, 'public/designs/themes', params[:theme])) | |
| 343 | 346 | params[:theme] |
| 344 | 347 | else |
| 345 | 348 | if profile && !profile.theme.nil? |
| ... | ... | @@ -371,6 +374,24 @@ module ApplicationHelper |
| 371 | 374 | nil |
| 372 | 375 | end |
| 373 | 376 | |
| 377 | + def theme_favicon | |
| 378 | + return '/designs/themes/' + current_theme + '/favicon.ico' if profile.nil? || profile.theme.nil? | |
| 379 | + if File.exists?(File.join(RAILS_ROOT, 'public', theme_path, 'favicon.ico')) | |
| 380 | + '/designs/themes/' + profile.theme + '/favicon.ico' | |
| 381 | + else | |
| 382 | + favicon = profile.articles.find_by_path('favicon.ico') | |
| 383 | + if favicon | |
| 384 | + favicon.public_filename | |
| 385 | + else | |
| 386 | + '/designs/themes/' + environment.theme + '/favicon.ico' | |
| 387 | + end | |
| 388 | + end | |
| 389 | + end | |
| 390 | + | |
| 391 | + def theme_site_title | |
| 392 | + theme_include('site_title') | |
| 393 | + end | |
| 394 | + | |
| 374 | 395 | def theme_header |
| 375 | 396 | theme_include('header') |
| 376 | 397 | end |
| ... | ... | @@ -395,6 +416,7 @@ module ApplicationHelper |
| 395 | 416 | # |
| 396 | 417 | # If the profile has no image set yet, then a default image is used. |
| 397 | 418 | def profile_image(profile, size=:portrait, opt={}) |
| 419 | + return '' if profile.nil? | |
| 398 | 420 | opt[:alt] ||= profile.name() |
| 399 | 421 | opt[:title] ||= '' |
| 400 | 422 | opt[:class] ||= '' |
| ... | ... | @@ -402,21 +424,33 @@ module ApplicationHelper |
| 402 | 424 | image_tag(profile_icon(profile, size), opt ) |
| 403 | 425 | end |
| 404 | 426 | |
| 405 | - def profile_icon( profile, size=:portrait ) | |
| 427 | + def profile_icon( profile, size=:portrait, return_mimetype=false ) | |
| 428 | + filename, mimetype = '', 'image/png' | |
| 406 | 429 | if profile.image |
| 407 | - profile.image.public_filename( size ) | |
| 430 | + filename = profile.image.public_filename( size ) | |
| 431 | + mimetype = profile.image.content_type | |
| 408 | 432 | else |
| 409 | - if profile.organization? | |
| 410 | - if profile.kind_of?(Community) | |
| 411 | - '/images/icons-app/users_size-'+ size.to_s() +'.png' | |
| 433 | + icon = | |
| 434 | + if profile.organization? | |
| 435 | + if profile.kind_of?(Community) | |
| 436 | + '/images/icons-app/community-'+ size.to_s() +'.png' | |
| 437 | + else | |
| 438 | + '/images/icons-app/enterprise-'+ size.to_s() +'.png' | |
| 439 | + end | |
| 412 | 440 | else |
| 413 | - '/images/icons-app/enterprise-default-pic-'+ size.to_s() +'.png' | |
| 441 | + '/images/icons-app/person-'+ size.to_s() +'.png' | |
| 414 | 442 | end |
| 415 | - else | |
| 416 | - '/images/icons-app/user_icon_size-'+ size.to_s() +'.png' | |
| 417 | - end | |
| 443 | + filename = default_or_themed_icon(icon) | |
| 418 | 444 | end |
| 445 | + return_mimetype ? [filename, mimetype] : filename | |
| 446 | + end | |
| 419 | 447 | |
| 448 | + def default_or_themed_icon(icon) | |
| 449 | + if File.exists?(File.join(Rails.root, 'public', theme_path, icon)) | |
| 450 | + theme_path + icon | |
| 451 | + else | |
| 452 | + icon | |
| 453 | + end | |
| 420 | 454 | end |
| 421 | 455 | |
| 422 | 456 | def profile_sex_icon( profile ) |
| ... | ... | @@ -455,71 +489,69 @@ module ApplicationHelper |
| 455 | 489 | end |
| 456 | 490 | end |
| 457 | 491 | |
| 458 | - # displays a link to add the profile with its image (as generated by | |
| 459 | - # #profile_image) or only its name below. | |
| 460 | - def profile_add_link( profile, image=false, size=:portrait, tag='li') | |
| 461 | - the_class = profile.members.include?(user) ? 'profile_member' : '' | |
| 462 | - name = h(profile.short_name) | |
| 463 | - if image | |
| 464 | - display = content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + | |
| 465 | - content_tag( 'span', name, :class => 'org' ) + | |
| 466 | - profile_cat_icons( profile ) | |
| 467 | - the_class << ' vcard' | |
| 468 | - else | |
| 469 | - display = content_tag( 'span', name, :class => 'org' ) | |
| 492 | + def links_for_balloon(profile) | |
| 493 | + if environment.enabled?(:show_balloon_with_profile_links_when_clicked) | |
| 494 | + if profile.kind_of?(Person) | |
| 495 | + [ | |
| 496 | + {_('Wall') => {:href => url_for(profile.public_profile_url)}}, | |
| 497 | + {_('Friends') => {:href => url_for(:controller => :profile, :action => :friends, :profile => profile.identifier)}}, | |
| 498 | + {_('Communities') => {:href => url_for(:controller => :profile, :action => :communities, :profile => profile.identifier)}}, | |
| 499 | + {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}}, | |
| 500 | + {_('Add') => {:href => url_for(profile.add_url), :class => 'add-friend', :style => 'display: none'}} | |
| 501 | + ] | |
| 502 | + elsif profile.kind_of?(Community) | |
| 503 | + [ | |
| 504 | + {_('Wall') => {:href => url_for(profile.public_profile_url)}}, | |
| 505 | + {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}}, | |
| 506 | + {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}}, | |
| 507 | + {_('Join') => {:href => url_for(profile.join_url), :class => 'join-community', :style => 'display: none'}}, | |
| 508 | + {_('Leave') => {:href => url_for(profile.leave_url), :class => 'leave-community', :style => 'display: none'}}, | |
| 509 | + {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}} | |
| 510 | + ] | |
| 511 | + elsif profile.kind_of?(Enterprise) | |
| 512 | + [ | |
| 513 | + {_('Products') => {:href => catalog_path(profile.identifier)}}, | |
| 514 | + {_('Members') => {:href => url_for(:controller => :profile, :action => :members, :profile => profile.identifier)}}, | |
| 515 | + {_('Agenda') => {:href => url_for(:controller => :profile, :action => :events, :profile => profile.identifier)}}, | |
| 516 | + {_('Send an e-mail') => {:href => url_for(:profile => profile.identifier, :controller => 'contact', :action => 'new'), :class => 'send-an-email', :style => 'display: none'}}, | |
| 517 | + ] | |
| 518 | + else | |
| 519 | + [] | |
| 520 | + end | |
| 470 | 521 | end |
| 471 | - content_tag tag, | |
| 472 | - link_to_remote( display, | |
| 473 | - :update => 'search-results-and-pages', | |
| 474 | - :url => {:controller => 'account', :action => 'profile_details', :profile => profile.identifier}, | |
| 475 | - :onclick => 'document.location.href = this.href', # work-arround for ie. | |
| 476 | - :class => 'profile_link url', | |
| 477 | - :help => _('Click on this icon to add <b>%s</b> to your network') % profile.name, | |
| 478 | - :title => profile.name ), | |
| 479 | - :class => the_class | |
| 480 | 522 | end |
| 481 | 523 | |
| 482 | 524 | # displays a link to the profile homepage with its image (as generated by |
| 483 | 525 | # #profile_image) and its name below it. |
| 484 | - def profile_image_link( profile, size=:portrait, tag='li' ) | |
| 485 | - if profile.class == Person | |
| 486 | - name = profile.short_name | |
| 487 | - city = content_tag 'span', content_tag( 'span', profile.city, :class => 'locality' ), :class => 'adr' | |
| 526 | + def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil ) | |
| 527 | + name = profile.short_name | |
| 528 | + if profile.person? | |
| 529 | + url = url_for(profile.check_friendship_url) | |
| 530 | + trigger_class = 'person-trigger' | |
| 488 | 531 | else |
| 489 | - name = profile.short_name | |
| 490 | 532 | city = '' |
| 533 | + url = url_for(profile.check_membership_url) | |
| 534 | + if profile.community? | |
| 535 | + trigger_class = 'community-trigger' | |
| 536 | + elsif profile.enterprise? | |
| 537 | + trigger_class = 'enterprise-trigger' | |
| 538 | + end | |
| 491 | 539 | end |
| 540 | + extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) | |
| 541 | + links = links_for_balloon(profile) | |
| 492 | 542 | content_tag tag, |
| 543 | + (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") + | |
| 493 | 544 | link_to( |
| 494 | 545 | content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + |
| 495 | 546 | content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) + |
| 496 | - city + profile_sex_icon( profile ) + profile_cat_icons( profile ), | |
| 547 | + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ), | |
| 497 | 548 | profile.url, |
| 498 | - :onclick => 'document.location.href = this.href', # work-arround for ie. | |
| 499 | 549 | :class => 'profile_link url', |
| 500 | 550 | :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, |
| 501 | 551 | :title => profile.name ), |
| 502 | 552 | :class => 'vcard' |
| 503 | 553 | end |
| 504 | 554 | |
| 505 | - # displays a link to the community homepage with its image (as generated by | |
| 506 | - # #profile_image) and its name and number of members beside it. | |
| 507 | - def community_image_link( profile, size=:portrait, tag='li' ) | |
| 508 | - name = h(profile.name) | |
| 509 | - content_tag tag, | |
| 510 | - link_to( | |
| 511 | - content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) + | |
| 512 | - content_tag( 'span', name, :class => 'org' ) + | |
| 513 | - content_tag( 'span', n_('1 member', '%s members', profile.members.count) % profile.members.count, :class => 'community-member-count' ), | |
| 514 | - profile.url, | |
| 515 | - :onclick => 'document.location.href = this.href', # work-arround for ie. | |
| 516 | - :class => 'profile_link url', | |
| 517 | - :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name, | |
| 518 | - :title => profile.name ) + | |
| 519 | - '<br class="may-clear"/>', | |
| 520 | - :class => 'vcard' | |
| 521 | - end | |
| 522 | - | |
| 523 | 555 | def gravatar_url_for(email, options = {}) |
| 524 | 556 | # Ta dando erro de roteamento |
| 525 | 557 | url_for( { :gravatar_id => Digest::MD5.hexdigest(email), |
| ... | ... | @@ -527,12 +559,12 @@ module ApplicationHelper |
| 527 | 559 | :protocol => 'http://', |
| 528 | 560 | :only_path => false, |
| 529 | 561 | :controller => 'avatar.php', |
| 530 | - :d => web2_conf['gravatar'] ? web2_conf['gravatar']['default'] : nil | |
| 562 | + :d => NOOSFERO_CONF['gravatar'] ? NOOSFERO_CONF['gravatar'] : nil | |
| 531 | 563 | }.merge(options) ) |
| 532 | 564 | end |
| 533 | 565 | |
| 534 | 566 | def str_gravatar_url_for(email, options = {}) |
| 535 | - default = web2_conf['gravatar'] ? web2_conf['gravatar']['default'] : nil | |
| 567 | + default = NOOSFERO_CONF['gravatar'] ? NOOSFERO_CONF['gravatar'] : nil | |
| 536 | 568 | url = 'http://www.gravatar.com/avatar.php?gravatar_id=' + |
| 537 | 569 | Digest::MD5.hexdigest(email) |
| 538 | 570 | { |
| ... | ... | @@ -591,7 +623,16 @@ module ApplicationHelper |
| 591 | 623 | end |
| 592 | 624 | |
| 593 | 625 | def select_folder(label, object, method, collection, html_options = {}, js_options = {}) |
| 594 | - labelled_form_field(label, select(object, method, collection.map {|f| [ profile.identifier + '/' + f.full_name, f.id ] }, html_options.merge({:include_blank => "#{profile.identifier}"}), js_options)) | |
| 626 | + root = profile ? profile.identifier : _("root") | |
| 627 | + labelled_form_field(label, select(object, method, | |
| 628 | + collection.map {|f| [ root + '/' + f.full_name, f.id ]}, | |
| 629 | + {:include_blank => root}, html_options.merge(js_options))) | |
| 630 | + end | |
| 631 | + | |
| 632 | + def select_profile_folder(label, object, method, profile, html_options = {}, js_options = {}) | |
| 633 | + labelled_form_field(label, select(object, method, | |
| 634 | + profile.folders.map {|f| [ profile.identifier + '/' + f.full_name, f.id ]}, | |
| 635 | + {:include_blank => profile.identifier}, html_options.merge(js_options))) | |
| 595 | 636 | end |
| 596 | 637 | |
| 597 | 638 | def theme_option(opt = nil) |
| ... | ... | @@ -851,6 +892,7 @@ module ApplicationHelper |
| 851 | 892 | |
| 852 | 893 | def helper_for_article(article) |
| 853 | 894 | article_helper = ActionView::Base.new |
| 895 | + article_helper.controller = controller | |
| 854 | 896 | article_helper.extend ArticleHelper |
| 855 | 897 | begin |
| 856 | 898 | class_name = article.class.name + 'Helper' |
| ... | ... | @@ -877,37 +919,27 @@ module ApplicationHelper |
| 877 | 919 | end |
| 878 | 920 | end |
| 879 | 921 | |
| 880 | - def ask_to_join? | |
| 881 | - return if !environment.enabled?(:join_community_popup) | |
| 882 | - return if params[:action] == 'join' | |
| 883 | - return unless profile && profile.kind_of?(Community) | |
| 884 | - if (session[:no_asking] && session[:no_asking].include?(profile.id)) | |
| 885 | - return false | |
| 886 | - end | |
| 887 | - if logged_in? | |
| 888 | - user.ask_to_join?(profile) | |
| 889 | - else | |
| 890 | - true | |
| 891 | - end | |
| 892 | - end | |
| 893 | - | |
| 894 | 922 | def icon_theme_stylesheet_path |
| 895 | - theme_path = "/designs/icons/#{environment.icon_theme}/style.css" | |
| 896 | - if File.exists?(File.join(RAILS_ROOT, 'public', theme_path)) | |
| 897 | - theme_path | |
| 898 | - else | |
| 899 | - '/designs/icons/default/style.css' | |
| 923 | + icon_themes = [] | |
| 924 | + theme_icon_themes = theme_option(:icon_theme) || [] | |
| 925 | + for icon_theme in theme_icon_themes do | |
| 926 | + theme_path = "/designs/icons/#{icon_theme}/style.css" | |
| 927 | + if File.exists?(File.join(RAILS_ROOT, 'public', theme_path)) | |
| 928 | + icon_themes << theme_path | |
| 929 | + end | |
| 900 | 930 | end |
| 931 | + icon_themes | |
| 901 | 932 | end |
| 902 | 933 | |
| 903 | 934 | def page_title |
| 904 | - (@page ? @page.name + ' - ' : '') + | |
| 935 | + (@page ? @page.title + ' - ' : '') + | |
| 905 | 936 | (profile ? profile.short_name + ' - ' : '') + |
| 906 | 937 | (@topic ? @topic.title + ' - ' : '') + |
| 907 | 938 | (@section ? @section.title + ' - ' : '') + |
| 908 | 939 | (@toc ? _('Online Manual') + ' - ' : '') + |
| 940 | + (@controller.controller_name == 'chat' ? _('Chat') + ' - ' : '') + | |
| 909 | 941 | environment.name + |
| 910 | - (@category ? "→ #{@category.full_name}" : '') | |
| 942 | + (@category ? " - #{@category.full_name}" : '') | |
| 911 | 943 | end |
| 912 | 944 | |
| 913 | 945 | def noosfero_javascript |
| ... | ... | @@ -946,7 +978,7 @@ module ApplicationHelper |
| 946 | 978 | end |
| 947 | 979 | |
| 948 | 980 | def article_to_html(article, options = {}) |
| 949 | - options.merge(:page => params[:npage]) | |
| 981 | + options.merge!(:page => params[:npage]) | |
| 950 | 982 | content = article.to_html(options) |
| 951 | 983 | return self.instance_eval(&content) if content.kind_of?(Proc) |
| 952 | 984 | content |
| ... | ... | @@ -960,4 +992,221 @@ module ApplicationHelper |
| 960 | 992 | text_field_tag(name, value, options.merge(:class => 'colorpicker_field')) |
| 961 | 993 | end |
| 962 | 994 | |
| 995 | + def ui_icon(icon_class, extra_class = '') | |
| 996 | + "<span class='ui-icon #{icon_class} #{extra_class}' style='float:left; margin-right:7px;'></span>" | |
| 997 | + end | |
| 998 | + | |
| 999 | + def ui_button(label, url, html_options = {}) | |
| 1000 | + link_to(label, url, html_options.merge(:class => 'ui_button fg-button')) | |
| 1001 | + end | |
| 1002 | + | |
| 1003 | + def ui_button_to_remote(label, options, html_options = {}) | |
| 1004 | + link_to_remote(label, options, html_options.merge(:class => 'ui_button fg-button')) | |
| 1005 | + end | |
| 1006 | + | |
| 1007 | + def jquery_theme | |
| 1008 | + theme_option(:jquery_theme) || 'smoothness_mod' | |
| 1009 | + end | |
| 1010 | + | |
| 1011 | + def jquery_ui_theme_stylesheet_path | |
| 1012 | + 'jquery.ui/' + jquery_theme + '/jquery-ui-1.8.2.custom' | |
| 1013 | + end | |
| 1014 | + | |
| 1015 | + def ui_error(message) | |
| 1016 | + content_tag('div', ui_icon('ui-icon-alert') + message, :class => 'alert fg-state-error ui-state-error') | |
| 1017 | + end | |
| 1018 | + | |
| 1019 | + def ui_highlight(message) | |
| 1020 | + content_tag('div', ui_icon('ui-icon-info') + message, :class => 'alert fg-state-highlight ui-state-highlight') | |
| 1021 | + end | |
| 1022 | + | |
| 1023 | + def float_to_currency(value) | |
| 1024 | + number_to_currency(value, :unit => environment.currency_unit, :separator => environment.currency_separator, :delimiter => environment.currency_delimiter, :format => "%u %n") | |
| 1025 | + end | |
| 1026 | + | |
| 1027 | + def collapsed_item_icon | |
| 1028 | + "<span class='ui-icon ui-icon-circlesmall-plus' style='float:left;'></span>" | |
| 1029 | + end | |
| 1030 | + def expanded_item_icon | |
| 1031 | + "<span class='ui-icon ui-icon-circlesmall-minus' style='float:left;'></span>" | |
| 1032 | + end | |
| 1033 | + def leaf_item_icon | |
| 1034 | + "<span class='ui-icon ui-icon-arrow-1-e' style='float:left;'></span>" | |
| 1035 | + end | |
| 1036 | + | |
| 1037 | + def display_category_menu(block, categories, root = true) | |
| 1038 | + categories = categories.sort{|x,y| x.name <=> y.name} | |
| 1039 | + return "" if categories.blank? | |
| 1040 | + content_tag(:ul, | |
| 1041 | + categories.map do |category| | |
| 1042 | + category_path = category.kind_of?(ProductCategory) ? {:controller => 'search', :action => 'assets', :asset => 'products', :product_category => category.id} : { :controller => 'search', :action => 'category_index', :category_path => category.explode_path } | |
| 1043 | + category.display_in_menu? ? | |
| 1044 | + content_tag(:li, | |
| 1045 | + ( !category.is_leaf_displayable_in_menu? ? content_tag(:a, collapsed_item_icon, :href => "#", :id => "block_#{block.id}_category_#{category.id}", :class => 'category-link-expand ' + (root ? 'category-root' : 'category-no-root'), :onclick => "expandCategory(#{block.id}, #{category.id}); return false", :style => 'display: none') : leaf_item_icon) + | |
| 1046 | + link_to(content_tag(:span, category.name, :class => 'category-name'), category_path, :class => ("category-leaf" if category.is_leaf_displayable_in_menu?)) + | |
| 1047 | + content_tag(:div, display_category_menu(block, category.children, false), :id => "block_#{block.id}_category_content_#{category.id}", :class => 'child-category') | |
| 1048 | + ) : '' | |
| 1049 | + end | |
| 1050 | + ) + | |
| 1051 | + content_tag(:p) + | |
| 1052 | + (root ? javascript_tag(" | |
| 1053 | + jQuery('.child-category').hide(); | |
| 1054 | + jQuery('.category-link-expand').show(); | |
| 1055 | + var expanded_icon = \"#{ expanded_item_icon }\"; | |
| 1056 | + var collapsed_icon = \"#{ collapsed_item_icon }\"; | |
| 1057 | + var category_expanded = { 'block' : 0, 'category' : 0 }; | |
| 1058 | + ") : '') | |
| 1059 | + end | |
| 1060 | + | |
| 1061 | + def browse_people_menu | |
| 1062 | + links = [ | |
| 1063 | + {s_('people|More Recent') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_recent'})}}, | |
| 1064 | + {s_('people|More Active') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_active'})}}, | |
| 1065 | + {s_('people|More Popular') => {:href => url_for({:controller => 'browse', :action => 'people', :filter => 'more_popular'})}} | |
| 1066 | + ] | |
| 1067 | + if logged_in? | |
| 1068 | + links.push(_('My friends') => {:href => url_for({:profile => current_user.login, :controller => 'friends'})}) | |
| 1069 | + links.push(_('Invite friends') => {:href => url_for({:profile => current_user.login, :controller => 'invite', :action => 'friends'})}) | |
| 1070 | + end | |
| 1071 | + | |
| 1072 | + link_to(content_tag(:span, _('People'), :class => 'icon-menu-people'), {:controller => "browse", :action => 'people'}, :id => 'submenu-people') + | |
| 1073 | + link_to(content_tag(:span, _('People Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-people-trigger') | |
| 1074 | + end | |
| 1075 | + | |
| 1076 | + def browse_communities_menu | |
| 1077 | + links = [ | |
| 1078 | + {s_('communities|More Recent') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_recent'})}}, | |
| 1079 | + {s_('communities|More Active') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_active'})}}, | |
| 1080 | + {s_('communities|More Popular') => {:href => url_for({:controller => 'browse', :action => 'communities', :filter => 'more_popular'})}} | |
| 1081 | + ] | |
| 1082 | + if logged_in? | |
| 1083 | + links.push(_('My communities') => {:href => url_for({:profile => current_user.login, :controller => 'memberships'})}) | |
| 1084 | + links.push(_('New community') => {:href => url_for({:profile => current_user.login, :controller => 'memberships', :action => 'new_community'})}) | |
| 1085 | + end | |
| 1086 | + | |
| 1087 | + link_to(content_tag(:span, _('Communities'), :class => 'icon-menu-community'), {:controller => "browse", :action => 'communities'}, :id => 'submenu-communities') + | |
| 1088 | + link_to(content_tag(:span, _('Communities Menu')), '#', :onclick => "toggleSubmenu(this,'',#{links.to_json}); return false", :class => 'menu-submenu-trigger up', :id => 'submenu-communities-trigger') | |
| 1089 | + end | |
| 1090 | + | |
| 1091 | + def pagination_links(collection, options={}) | |
| 1092 | + options = {:prev_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options) | |
| 1093 | + will_paginate(collection, options) | |
| 1094 | + end | |
| 1095 | + | |
| 1096 | + def render_environment_features(folder) | |
| 1097 | + result = '' | |
| 1098 | + environment.enabled_features.keys.each do |feature| | |
| 1099 | + file = File.join(@controller.view_paths, 'shared', folder.to_s, "#{feature}.rhtml") | |
| 1100 | + if File.exists?(file) | |
| 1101 | + result << render(:file => file, :use_full_path => false) | |
| 1102 | + end | |
| 1103 | + end | |
| 1104 | + result | |
| 1105 | + end | |
| 1106 | + | |
| 1107 | + def usermenu_logged_in | |
| 1108 | + pending_tasks_count = '' | |
| 1109 | + if user && user.all_pending_tasks.count > 0 | |
| 1110 | + pending_tasks_count = link_to(user.all_pending_tasks.count.to_s, '/myprofile/{login}/tasks', :id => 'pending-tasks-count', :title => _("Manage your pending tasks")) | |
| 1111 | + end | |
| 1112 | + | |
| 1113 | + (_('Welcome, %s') % link_to('<i></i><strong>{login}</strong>', '/{login}', :id => "homepage-link", :title => _('Go to your homepage'))) + | |
| 1114 | + render_environment_features(:usermenu) + | |
| 1115 | + link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', { :controller => 'admin_panel', :action => 'index' }, :id => "controlpanel", :title => _("Configure the environment"), :class => 'admin-link', :style => 'display: none') + | |
| 1116 | + link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', '/myprofile/{login}', :id => "controlpanel", :title => _("Configure your personal account and content")) + | |
| 1117 | + pending_tasks_count + | |
| 1118 | + link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) | |
| 1119 | + end | |
| 1120 | + | |
| 1121 | + def limited_text_area(object_name, method, limit, text_area_id, options = {}) | |
| 1122 | + content_tag(:div, [ | |
| 1123 | + text_area(object_name, method, { :id => text_area_id, :onkeyup => "limited_text_area('#{text_area_id}', #{limit})" }.merge(options)), | |
| 1124 | + content_tag(:p, content_tag(:span, limit) + ' ' + _(' characters left'), :id => text_area_id + '_left'), | |
| 1125 | + content_tag(:p, _('Limit of characters reached'), :id => text_area_id + '_limit', :style => 'display: none') | |
| 1126 | + ], :class => 'limited-text-area') | |
| 1127 | + end | |
| 1128 | + | |
| 1129 | + def pluralize_without_count(count, singular, plural = nil) | |
| 1130 | + count == 1 ? singular : (plural || singular.pluralize) | |
| 1131 | + end | |
| 1132 | + | |
| 1133 | + def unique_with_count(list, connector = 'for') | |
| 1134 | + list.sort.inject(Hash.new(0)){|h,i| h[i] += 1; h }.collect{ |x, n| [n, connector, x].join(" ") }.sort | |
| 1135 | + end | |
| 1136 | + | |
| 1137 | + #FIXME Use time_ago_in_words instead of this method if you're using Rails 2.2+ | |
| 1138 | + def time_ago_as_sentence(from_time, include_seconds = false) | |
| 1139 | + to_time = Time.now | |
| 1140 | + from_time = from_time.to_time if from_time.respond_to?(:to_time) | |
| 1141 | + to_time = to_time.to_time if to_time.respond_to?(:to_time) | |
| 1142 | + distance_in_minutes = (((to_time - from_time).abs)/60).round | |
| 1143 | + distance_in_seconds = ((to_time - from_time).abs).round | |
| 1144 | + case distance_in_minutes | |
| 1145 | + when 0..1 | |
| 1146 | + return (distance_in_minutes == 0) ? _('less than a minute') : _('1 minute') unless include_seconds | |
| 1147 | + case distance_in_seconds | |
| 1148 | + when 0..4 then _('less than 5 seconds') | |
| 1149 | + when 5..9 then _('less than 10 seconds') | |
| 1150 | + when 10..19 then _('less than 20 seconds') | |
| 1151 | + when 20..39 then _('half a minute') | |
| 1152 | + when 40..59 then _('less than a minute') | |
| 1153 | + else _('1 minute') | |
| 1154 | + end | |
| 1155 | + | |
| 1156 | + when 2..44 then _('%{distance} minutes') % { :distance => distance_in_minutes } | |
| 1157 | + when 45..89 then _('about 1 hour') | |
| 1158 | + when 90..1439 then _('about %{distance} hours') % { :distance => (distance_in_minutes.to_f / 60.0).round } | |
| 1159 | + when 1440..2879 then _('1 day') | |
| 1160 | + when 2880..43199 then _('%{distance} days') % { :distance => (distance_in_minutes / 1440).round } | |
| 1161 | + when 43200..86399 then _('about 1 month') | |
| 1162 | + when 86400..525599 then _('%{distance} months') % { :distance => (distance_in_minutes / 43200).round } | |
| 1163 | + when 525600..1051199 then _('about 1 year') | |
| 1164 | + else _('over %{distance} years') % { :distance => (distance_in_minutes / 525600).round } | |
| 1165 | + end | |
| 1166 | + end | |
| 1167 | + | |
| 1168 | + def comment_balloon(options = {}, &block) | |
| 1169 | + wrapper = content_tag(:div, capture(&block), :class => 'comment-balloon-content') | |
| 1170 | + (1..8).to_a.reverse.each { |i| wrapper = content_tag(:div, wrapper, :class => "comment-wrapper-#{i}") } | |
| 1171 | + classes = options.delete(:class) || options.delete("class") || '' | |
| 1172 | + concat(content_tag('div', wrapper + tag('br', :style => 'clear: both;'), { :class => 'comment-balloon ' + classes.to_s }.merge(options)), block.binding) | |
| 1173 | + end | |
| 1174 | + | |
| 1175 | + def display_source_info(page) | |
| 1176 | + if !page.source.blank? | |
| 1177 | + source_url = link_to(page.source_name.blank? ? page.source : page.source_name, page.source) | |
| 1178 | + elsif page.reference_article | |
| 1179 | + source_url = link_to(page.reference_article.profile.name, page.reference_article.url) | |
| 1180 | + end | |
| 1181 | + content_tag(:div, _('Source: %s') % source_url, :id => 'article-source') unless source_url.nil? | |
| 1182 | + end | |
| 1183 | + | |
| 1184 | + def task_information(task) | |
| 1185 | + values = {} | |
| 1186 | + values.merge!({:requestor => link_to(task.requestor.name, task.requestor.public_profile_url)}) if task.requestor | |
| 1187 | + values.merge!({:subject => content_tag('span', task.subject, :class=>'task_target')}) if task.subject | |
| 1188 | + values.merge!({:linked_subject => link_to(content_tag('span', task.linked_subject[:text], :class => 'task_target'), task.linked_subject[:url])}) if task.linked_subject | |
| 1189 | + values.merge!(task.information[:variables]) if task.information[:variables] | |
| 1190 | + task.information[:message] % values | |
| 1191 | + end | |
| 1192 | + | |
| 1193 | + def add_zoom_to_images | |
| 1194 | + if environment.enabled?(:show_zoom_button_on_article_images) | |
| 1195 | + stylesheet_link_tag('fancybox') + | |
| 1196 | + javascript_include_tag('jquery.fancybox-1.3.4.pack') + | |
| 1197 | + javascript_tag("jQuery(function($) { | |
| 1198 | + $('#article .article-body img').each( function(index) { | |
| 1199 | + var original = original_image_dimensions($(this).attr('src')); | |
| 1200 | + if ($(this).width() < original['width'] || $(this).height() < original['height']) { | |
| 1201 | + $(this).wrap('<div class=\"zoomable-image\" />'); | |
| 1202 | + $(this).parent('.zoomable-image').attr('style', $(this).attr('style')); | |
| 1203 | + $(this).attr('style', ''); | |
| 1204 | + $(this).after(\'<a href=\"' + $(this).attr('src') + '\" class=\"zoomify-image\"><span class=\"zoomify-text\">%s</span></a>'); | |
| 1205 | + } | |
| 1206 | + }); | |
| 1207 | + $('.zoomify-image').fancybox(); | |
| 1208 | + });" % _('Zoom in')) | |
| 1209 | + end | |
| 1210 | + end | |
| 1211 | + | |
| 963 | 1212 | end | ... | ... |
app/helpers/article_helper.rb
| ... | ... | @@ -8,12 +8,13 @@ module ArticleHelper |
| 8 | 8 | 'div', |
| 9 | 9 | check_box(:article, :published) + |
| 10 | 10 | content_tag('label', _('This article must be published (visible to other people)'), :for => 'article_published') |
| 11 | - ) + | |
| 11 | + ) + (article.parent && article.parent.forum? && controller.action_name == 'new' ? | |
| 12 | + hidden_field_tag('article[accept_comments]', 1) : | |
| 12 | 13 | content_tag( |
| 13 | 14 | 'div', |
| 14 | 15 | check_box(:article, :accept_comments) + |
| 15 | - content_tag('label', _('I want to receive comments about this article'), :for => 'article_accept_comments') | |
| 16 | - ) + | |
| 16 | + content_tag('label', (article.parent && article.parent.forum? ? _('This topic is opened for replies') : _('I want to receive comments about this article')), :for => 'article_accept_comments') | |
| 17 | + )) + | |
| 17 | 18 | content_tag( |
| 18 | 19 | 'div', |
| 19 | 20 | check_box(:article, :notify_comments) + | ... | ... |
app/helpers/assets_helper.rb
| ... | ... | @@ -9,7 +9,7 @@ module AssetsHelper |
| 9 | 9 | [ options.merge(:asset => 'products'), "icon-menu-product", _('Products') ], |
| 10 | 10 | [ options.merge(:asset => 'enterprises'), "icon-menu-enterprise", __('Enterprises') ], |
| 11 | 11 | [ options.merge(:asset => 'communities'), "icon-menu-community", __('Communities') ], |
| 12 | - [ options.merge(:asset => 'events'), "icon-menu-events", __('Events') ], | |
| 12 | + [ options.merge(:asset => 'events'), "icon-event", __('Events') ], | |
| 13 | 13 | |
| 14 | 14 | ].select do |target, css_class, name| |
| 15 | 15 | !environment.enabled?('disable_asset_' + target[:asset]) | ... | ... |
app/helpers/block_helper.rb
app/helpers/blog_helper.rb
| ... | ... | @@ -11,7 +11,7 @@ module BlogHelper |
| 11 | 11 | end |
| 12 | 12 | |
| 13 | 13 | def cms_label_for_edit |
| 14 | - _('Edit blog') | |
| 14 | + _('Configure blog') | |
| 15 | 15 | end |
| 16 | 16 | |
| 17 | 17 | def list_posts(articles, format = 'full') |
| ... | ... | @@ -48,7 +48,7 @@ module BlogHelper |
| 48 | 48 | |
| 49 | 49 | def display_short_format(article) |
| 50 | 50 | html = content_tag('div', |
| 51 | - article.first_paragraph + | |
| 51 | + article.lead + | |
| 52 | 52 | content_tag('div', |
| 53 | 53 | link_to_comments(article) + |
| 54 | 54 | link_to( _('Read more'), article.url), | ... | ... |
app/helpers/boxes_helper.rb
| ... | ... | @@ -65,7 +65,7 @@ module BoxesHelper |
| 65 | 65 | end |
| 66 | 66 | |
| 67 | 67 | def display_box_content(box, main_content) |
| 68 | - context = { :article => @page } | |
| 68 | + context = { :article => @page, :request_path => request.path, :locale => locale } | |
| 69 | 69 | box_decorator.select_blocks(box.blocks, context).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box) |
| 70 | 70 | end |
| 71 | 71 | ... | ... |
app/helpers/catalog_helper.rb
| 1 | 1 | module CatalogHelper |
| 2 | 2 | |
| 3 | 3 | include DisplayHelper |
| 4 | +include ManageProductsHelper | |
| 4 | 5 | |
| 5 | 6 | def display_products_list(profile, products) |
| 6 | 7 | data = '' |
| ... | ... | @@ -19,7 +20,7 @@ include DisplayHelper |
| 19 | 20 | content_tag('h1', _('Products/Services')) + content_tag('ul', data, :id => 'product_list') |
| 20 | 21 | end |
| 21 | 22 | |
| 22 | -private | |
| 23 | + private | |
| 23 | 24 | |
| 24 | 25 | def product_category_name(profile, product_category) |
| 25 | 26 | if profile.enabled? | ... | ... |
app/helpers/categories_helper.rb
| ... | ... | @@ -34,8 +34,7 @@ module CategoriesHelper |
| 34 | 34 | |
| 35 | 35 | def select_category_type(field) |
| 36 | 36 | value = params[field] |
| 37 | - types = TYPES.select { |title,typename| environment.category_types.include?(typename) } | |
| 38 | - labelled_form_field(_('Type of category'), select_tag('type', options_for_select(types, value))) | |
| 37 | + labelled_form_field(_('Type of category'), select_tag('type', options_for_select(TYPES, value))) | |
| 39 | 38 | end |
| 40 | 39 | |
| 41 | 40 | end | ... | ... |
| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | +module ChatHelper | |
| 2 | + | |
| 3 | + def user_status_menu(icon_class, status) | |
| 4 | + links = [ | |
| 5 | + ['icon-menu-online', _('Online'), 'chat-connect'], | |
| 6 | + ['icon-menu-busy', _('Busy'), 'chat-busy'], | |
| 7 | + ['icon-menu-offline', _('Sign out of chat'), 'chat-disconnect'], | |
| 8 | + ] | |
| 9 | + content_tag('span', | |
| 10 | + link_to(content_tag('span', status) + ui_icon('ui-icon-triangle-1-s'), | |
| 11 | + '#', | |
| 12 | + :onclick => 'toggleMenu(this); return false', | |
| 13 | + :class => icon_class + ' simplemenu-trigger' | |
| 14 | + ) + | |
| 15 | + content_tag('ul', | |
| 16 | + links.map{|link| content_tag('li', link_to(link[1], '#', :class => link[0], :id => link[2], 'data-jid' => user.jid), :class => 'simplemenu-item') }.join("\n"), | |
| 17 | + :style => 'display: none; z-index: 100', | |
| 18 | + :class => 'simplemenu-submenu' | |
| 19 | + ), | |
| 20 | + :class => 'user-status' | |
| 21 | + ) | |
| 22 | + end | |
| 23 | + | |
| 24 | +end | ... | ... |
app/helpers/cms_helper.rb
| ... | ... | @@ -27,4 +27,34 @@ module CmsHelper |
| 27 | 27 | article_helper.custom_options_for_article(article) |
| 28 | 28 | end |
| 29 | 29 | |
| 30 | + def link_to_article(article) | |
| 31 | + article_name = short_filename(article.title, 30) | |
| 32 | + if article.folder? | |
| 33 | + link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article) | |
| 34 | + else | |
| 35 | + if article.image? | |
| 36 | + image_tag(icon_for_article(article)) + link_to(article_name, article.url) | |
| 37 | + else | |
| 38 | + link_to article_name, article.url, :class => icon_for_article(article) | |
| 39 | + end | |
| 40 | + end | |
| 41 | + end | |
| 42 | + | |
| 43 | + def display_spread_button(profile, article) | |
| 44 | + if profile.person? | |
| 45 | + button_without_text :spread, _('Spread this'), :action => 'publish', :id => article.id | |
| 46 | + elsif profile.community? && environment.portal_community | |
| 47 | + button_without_text :spread, _('Spread this'), :action => 'publish_on_portal_community', :id => article.id | |
| 48 | + end | |
| 49 | + end | |
| 50 | + | |
| 51 | + def display_delete_button(article) | |
| 52 | + confirm_message = if article.folder? | |
| 53 | + _('Are you sure that you want to remove this folder? Note that all the items inside it will also be removed!') | |
| 54 | + else | |
| 55 | + _('Are you sure that you want to remove this item?') | |
| 56 | + end | |
| 57 | + | |
| 58 | + button_without_text :delete, _('Delete'), { :action => 'destroy', :id => article.id }, :method => :post, :confirm => confirm_message | |
| 59 | + end | |
| 30 | 60 | end | ... | ... |
app/helpers/content_viewer_helper.rb
| 1 | 1 | module ContentViewerHelper |
| 2 | 2 | |
| 3 | 3 | include BlogHelper |
| 4 | + include ForumHelper | |
| 4 | 5 | |
| 5 | 6 | def number_of_comments(article) |
| 6 | 7 | n = article.comments.size |
| ... | ... | @@ -20,7 +21,7 @@ module ContentViewerHelper |
| 20 | 21 | title = content_tag('h1', link_to(article.name, article.url), :class => 'title') |
| 21 | 22 | end |
| 22 | 23 | comments = args[:no_comments] ? '' : (("- %s") % link_to_comments(article)) |
| 23 | - title << content_tag('span', _("%s, by %s %s") % [show_date(article.published_at), link_to(article.author.name, article.author.url), comments], :class => 'created-at') | |
| 24 | + title << content_tag('span', _("%s, by %s %s") % [show_date(article.published_at), link_to(article.author_name, article.author.url), comments], :class => 'created-at') | |
| 24 | 25 | end |
| 25 | 26 | title |
| 26 | 27 | end |
| ... | ... | @@ -30,8 +31,20 @@ module ContentViewerHelper |
| 30 | 31 | end |
| 31 | 32 | |
| 32 | 33 | def image_label(image) |
| 33 | - text = image.title || image.abstract | |
| 34 | + text = image.abstract || image.title | |
| 34 | 35 | text && (text.first(40) + (text.size > 40 ? '…' : '')) |
| 35 | 36 | end |
| 36 | 37 | |
| 38 | + def article_translations(article) | |
| 39 | + unless article.native_translation.translations.empty? | |
| 40 | + links = (article.native_translation.translations + [article.native_translation]).map do |translation| | |
| 41 | + { Noosfero.locales[translation.language] => { :href => url_for(translation.url) } } | |
| 42 | + end | |
| 43 | + content_tag(:div, link_to(_('Translations'), '#', | |
| 44 | + :onclick => "toggleSubmenu(this, '#{_('Translations')}', #{links.to_json}); return false", | |
| 45 | + :class => 'article-translations-menu simplemenu-trigger up'), | |
| 46 | + :class => 'article-translations') | |
| 47 | + end | |
| 48 | + end | |
| 49 | + | |
| 37 | 50 | end | ... | ... |
app/helpers/dates_helper.rb
| ... | ... | @@ -36,7 +36,7 @@ module DatesHelper |
| 36 | 36 | # formats a datetime for displaying. |
| 37 | 37 | def show_time(time) |
| 38 | 38 | if time |
| 39 | - _('%{day} %{month} %{year}, %{hour}:%{minutes}') % { :year => time.year, :month => month_name(time.month), :day => time.day, :hour => time.hour, :minutes => time.min } | |
| 39 | + _('%{day} %{month} %{year}, %{hour}:%{minutes}') % { :year => time.year, :month => month_name(time.month), :day => time.day, :hour => time.hour, :minutes => time.strftime("%M") } | |
| 40 | 40 | else |
| 41 | 41 | '' |
| 42 | 42 | end |
| ... | ... | @@ -46,7 +46,7 @@ module DatesHelper |
| 46 | 46 | if (date1 == date2) || (date2.nil?) |
| 47 | 47 | show_date(date1) |
| 48 | 48 | else |
| 49 | - _('from %s to %s') % [show_date(date1), show_date(date2)] | |
| 49 | + _('from %{date1} to %{date2}') % {:date1 => show_date(date1), :date2 => show_date(date2)} | |
| 50 | 50 | end |
| 51 | 51 | end |
| 52 | 52 | ... | ... |
app/helpers/display_helper.rb
| ... | ... | @@ -2,12 +2,16 @@ module DisplayHelper |
| 2 | 2 | |
| 3 | 3 | def link_to_product(product, opts={}) |
| 4 | 4 | return _('No product') unless product |
| 5 | - target = product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'catalog', :action => 'show', :id => product) : product.enterprise.url | |
| 5 | + target = product_path(product) | |
| 6 | 6 | link_to content_tag( 'span', product.name ), |
| 7 | 7 | target, |
| 8 | 8 | opts |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | + def product_path(product) | |
| 12 | + product.enterprise.enabled? ? product.enterprise.public_profile_url.merge(:controller => 'manage_products', :action => 'show', :id => product) : product.enterprise.url | |
| 13 | + end | |
| 14 | + | |
| 11 | 15 | def link_to_category(category, full = true) |
| 12 | 16 | return _('Uncategorized product') unless category |
| 13 | 17 | name = full ? category.full_name(' → ') : category.name |
| ... | ... | @@ -27,9 +31,10 @@ module DisplayHelper |
| 27 | 31 | gsub( /\n\s*\n/, ' <p/> ' ). |
| 28 | 32 | gsub( /\n/, ' <br/> ' ). |
| 29 | 33 | gsub( /(^|\s)(www\.[^\s])/, '\1http://\2' ). |
| 30 | - gsub( /(https?:\/\/([^\s]+))/, | |
| 31 | - '<a href="\1" target="_blank" rel="nofolow" onclick="return confirm(\'' + | |
| 32 | - escape_javascript( _('Are you sure you want to visit this web site?') ) + | |
| 33 | - '\n\n\'+this.href)">\2</a>' ) | |
| 34 | + gsub( /(https?:\/\/([^\s]+))/ ) do | |
| 35 | + href, content = $1, $2.scan(/.{4}/).join('​') | |
| 36 | + content_tag(:a, content, :href => href, :target => '_blank', :rel => 'nofolow', | |
| 37 | + :onclick => "return confirm('%s')" % escape_javascript(_('Are you sure you want to visit this web site?'))) | |
| 38 | + end | |
| 34 | 39 | end |
| 35 | 40 | end | ... | ... |
app/helpers/events_helper.rb
| ... | ... | @@ -6,7 +6,7 @@ module EventsHelper |
| 6 | 6 | content_tag('h2', title) + |
| 7 | 7 | content_tag('div', |
| 8 | 8 | (events.any? ? |
| 9 | - content_tag('table', events.select { |item| item.public? }.map {|item| display_event_in_listing(item)}.join('')) : | |
| 9 | + content_tag('table', events.select { |item| item.display_to?(user) }.map {|item| display_event_in_listing(item)}.join('')) : | |
| 10 | 10 | content_tag('em', _('No events for this date'), :class => 'no-events') |
| 11 | 11 | ), :id => 'agenda-items' |
| 12 | 12 | ) |
| ... | ... | @@ -15,7 +15,7 @@ module EventsHelper |
| 15 | 15 | def display_event_in_listing(article) |
| 16 | 16 | content_tag( |
| 17 | 17 | 'tr', |
| 18 | - content_tag('td', link_to(image_tag(icon_for_article(article)) + article.name, article.url)), | |
| 18 | + content_tag('td', link_to(article.name, article.url, :class => icon_for_article(article))), | |
| 19 | 19 | :class => 'agenda-item' |
| 20 | 20 | ) |
| 21 | 21 | end |
| ... | ... | @@ -26,7 +26,7 @@ module EventsHelper |
| 26 | 26 | # the day itself |
| 27 | 27 | date, |
| 28 | 28 | # is there any events in this date? |
| 29 | - events.any? do |event| | |
| 29 | + events.select {|event| event.display_to?(user)}.any? do |event| | |
| 30 | 30 | event.date_range.include?(date) |
| 31 | 31 | end, |
| 32 | 32 | # is this date in the current month? | ... | ... |
| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | +module FloatHelper | |
| 2 | + | |
| 3 | + def decimal_to_float( num ) | |
| 4 | + if num.count('.') == 1 && num.count(',') == 0 | |
| 5 | + # number like "12.34" | |
| 6 | + return num.to_f | |
| 7 | + end | |
| 8 | + | |
| 9 | + if num.count('.') == 0 && num.count(',') == 1 | |
| 10 | + # number like "12,34" | |
| 11 | + return num.tr(',','.').to_f | |
| 12 | + end | |
| 13 | + | |
| 14 | + if num.count('.') > 0 && num.count(',') > 0 | |
| 15 | + # number like "12.345.678,90" or "12,345,678.90" | |
| 16 | + dec_sep = num.tr('0-9','')[-1].chr | |
| 17 | + return num.tr('^0-9'+dec_sep,'').tr(dec_sep,'.').to_f | |
| 18 | + end | |
| 19 | + | |
| 20 | + # if you are here is because there is only one | |
| 21 | + # separator and this appears 2 times or more. | |
| 22 | + # number like "12.345.678" or "12,345,678" | |
| 23 | + | |
| 24 | + return num.tr(',.','').to_f | |
| 25 | + end | |
| 26 | + | |
| 27 | +end | ... | ... |
app/helpers/folder_helper.rb
| 1 | 1 | module FolderHelper |
| 2 | 2 | |
| 3 | + include ShortFilename | |
| 4 | + | |
| 3 | 5 | def list_articles(articles, recursive = false) |
| 4 | 6 | if !articles.blank? |
| 5 | - content_tag( | |
| 6 | - 'table', | |
| 7 | - content_tag('tr', content_tag('th', _('Title')) + content_tag('th', _('Last update'))) + | |
| 8 | - articles.map {|item| display_article_in_listing(item, recursive, 0)}.join('') | |
| 7 | + articles = articles.paginate( | |
| 8 | + :order => "updated_at DESC", | |
| 9 | + :per_page => 10, | |
| 10 | + :page => params[:npage] | |
| 9 | 11 | ) |
| 12 | + | |
| 13 | + render :file => 'shared/articles_list', :locals => {:articles => articles, :recursive => recursive} | |
| 10 | 14 | else |
| 11 | 15 | content_tag('em', _('(empty folder)')) |
| 12 | 16 | end |
| ... | ... | @@ -17,9 +21,14 @@ module FolderHelper |
| 17 | 21 | end |
| 18 | 22 | |
| 19 | 23 | def display_article_in_listing(article, recursive = false, level = 0) |
| 24 | + article_link = if article.image? | |
| 25 | + link_to(' ' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true)) | |
| 26 | + else | |
| 27 | + link_to(' ' * (level * 4) + short_filename(article.name), article.url.merge(:view => true), :class => icon_for_article(article)) | |
| 28 | + end | |
| 20 | 29 | result = content_tag( |
| 21 | 30 | 'tr', |
| 22 | - content_tag('td', link_to((' ' * (level * 4) ) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true)))+ | |
| 31 | + content_tag('td', article_link )+ | |
| 23 | 32 | content_tag('td', show_date(article.updated_at), :class => 'last-update'), |
| 24 | 33 | :class => 'sitemap-item' |
| 25 | 34 | ) |
| ... | ... | @@ -31,18 +40,22 @@ module FolderHelper |
| 31 | 40 | end |
| 32 | 41 | |
| 33 | 42 | def icon_for_article(article) |
| 34 | - icon = article.icon_name | |
| 43 | + icon = article.class.icon_name(article) | |
| 35 | 44 | if (icon =~ /\//) |
| 36 | 45 | icon |
| 37 | 46 | else |
| 38 | - if File.exists?(File.join(RAILS_ROOT, 'public', 'images', 'icons-mime', "#{icon}.png")) | |
| 39 | - "icons-mime/#{icon}.png" | |
| 40 | - else | |
| 41 | - "icons-mime/unknown.png" | |
| 47 | + klasses = 'icon icon-' + icon | |
| 48 | + if article.kind_of?(UploadedFile) | |
| 49 | + klasses += ' icon-upload-file' | |
| 42 | 50 | end |
| 51 | + klasses | |
| 43 | 52 | end |
| 44 | 53 | end |
| 45 | 54 | |
| 55 | + def icon_for_new_article(type) | |
| 56 | + "icon-new icon-new%s" % type.constantize.icon_name | |
| 57 | + end | |
| 58 | + | |
| 46 | 59 | def custom_options_for_article(article) |
| 47 | 60 | @article = article |
| 48 | 61 | content_tag('h4', _('Options')) + |
| ... | ... | @@ -69,11 +82,4 @@ module FolderHelper |
| 69 | 82 | _('Edit folder') |
| 70 | 83 | end |
| 71 | 84 | |
| 72 | - def short_filename(filename, limit_chars = 43) | |
| 73 | - return filename if filename.size <= limit_chars | |
| 74 | - extname = File.extname(filename) | |
| 75 | - basename = File.basename(filename,extname) | |
| 76 | - str_complement = '(...)' | |
| 77 | - return basename[0..(limit_chars - extname.size - str_complement.size - 1)] + str_complement + extname | |
| 78 | - end | |
| 79 | 85 | end | ... | ... |
app/helpers/forms_helper.rb
| ... | ... | @@ -41,8 +41,7 @@ module FormsHelper |
| 41 | 41 | the_class << ' ' << html_options[:class] |
| 42 | 42 | end |
| 43 | 43 | |
| 44 | - # FIXME: should be in stylesheet | |
| 45 | - bt_submit = submit_tag(label, html_options.merge(:style => 'height:28px; cursor:pointer', :class => the_class)) | |
| 44 | + bt_submit = submit_tag(label, html_options.merge(:class => the_class)) | |
| 46 | 45 | |
| 47 | 46 | bt_submit + bt_cancel |
| 48 | 47 | end |
| ... | ... | @@ -60,6 +59,10 @@ module FormsHelper |
| 60 | 59 | |
| 61 | 60 | state_id = 'state-' + FormsHelper.next_id_number |
| 62 | 61 | city_id = 'city-' + FormsHelper.next_id_number |
| 62 | + | |
| 63 | + if states.length < 1 | |
| 64 | + return | |
| 65 | + end | |
| 63 | 66 | |
| 64 | 67 | if simple |
| 65 | 68 | states = [State.new(:name => _('Select the State'))] + states |
| ... | ... | @@ -108,6 +111,18 @@ module FormsHelper |
| 108 | 111 | )) |
| 109 | 112 | end |
| 110 | 113 | |
| 114 | + def options_for_select_with_title(container, selected = nil) | |
| 115 | + container = container.to_a if Hash === container | |
| 116 | + | |
| 117 | + options_for_select = container.inject([]) do |options, element| | |
| 118 | + text, value = option_text_and_value(element) | |
| 119 | + selected_attribute = ' selected="selected"' if option_value_selected?(value, selected) | |
| 120 | + options << %(<option title="#{html_escape(text.to_s)}" value="#{html_escape(value.to_s)}"#{selected_attribute}>#{html_escape(text.to_s)}</option>) | |
| 121 | + end | |
| 122 | + | |
| 123 | + options_for_select.join("\n") | |
| 124 | + end | |
| 125 | + | |
| 111 | 126 | protected |
| 112 | 127 | def self.next_id_number |
| 113 | 128 | if defined? @@id_num | ... | ... |
| ... | ... | @@ -0,0 +1,51 @@ |
| 1 | +module ForumHelper | |
| 2 | + | |
| 3 | + def cms_label_for_new_children | |
| 4 | + _('New discussion topic') | |
| 5 | + end | |
| 6 | + | |
| 7 | + def cms_label_for_edit | |
| 8 | + _('Configure forum') | |
| 9 | + end | |
| 10 | + | |
| 11 | + def list_forum_posts(articles) | |
| 12 | + pagination = will_paginate(articles, { | |
| 13 | + :param_name => 'npage', | |
| 14 | + :prev_label => _('« Newer posts'), | |
| 15 | + :next_label => _('Older posts »') | |
| 16 | + }) | |
| 17 | + content = [content_tag('tr', | |
| 18 | + content_tag('th', _('Discussion topic')) + | |
| 19 | + content_tag('th', _('Posts')) + | |
| 20 | + content_tag('th', _('Last post')) | |
| 21 | + ) | |
| 22 | + ] | |
| 23 | + artic_len = articles.length | |
| 24 | + articles.each_with_index{ |art,i| | |
| 25 | + css_add = [ 'position-'+(i+1).to_s() ] | |
| 26 | + position = (i%2 == 0) ? 'odd-post' : 'even-post' | |
| 27 | + css_add << 'first' if i == 0 | |
| 28 | + css_add << 'last' if i == (artic_len-1) | |
| 29 | + css_add << 'not-published' if !art.published? | |
| 30 | + css_add << position | |
| 31 | + content << content_tag('tr', | |
| 32 | + content_tag('td', link_to(art.title, art.url)) + | |
| 33 | + content_tag('td', link_to(art.comments.count, art.url.merge(:anchor => 'comments_list'))) + | |
| 34 | + content_tag('td', last_topic_update(art)), | |
| 35 | + :class => 'forum-post ' + css_add.join(' '), | |
| 36 | + :id => "post-#{art.id}" | |
| 37 | + ) | |
| 38 | + } | |
| 39 | + content_tag('table', content) + (pagination or '') | |
| 40 | + end | |
| 41 | + | |
| 42 | + def last_topic_update(article) | |
| 43 | + info = article.info_from_last_update | |
| 44 | + if info[:author_url] | |
| 45 | + time_ago_as_sentence(info[:date]) + ' ' + _('ago') + ' ' + _('by') + ' ' + link_to(info[:author_name], info[:author_url]) | |
| 46 | + else | |
| 47 | + time_ago_as_sentence(info[:date]) + ' ' + _('ago') + ' ' + _('by') + ' ' + info[:author_name] | |
| 48 | + end | |
| 49 | + end | |
| 50 | + | |
| 51 | +end | ... | ... |
app/helpers/friends_helper.rb
| 1 | 1 | module FriendsHelper |
| 2 | 2 | |
| 3 | - def link_to_import(text, options = {}) | |
| 4 | - options.merge!({:action => 'invite', :import => 1, :wizard => true}) | |
| 5 | - link_to text, options | |
| 6 | - end | |
| 7 | - | |
| 8 | - def pagination_links(collection, options={}) | |
| 9 | - options = {:prev_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options) | |
| 10 | - will_paginate(collection, options) | |
| 11 | - end | |
| 12 | - | |
| 13 | 3 | end | ... | ... |
app/helpers/manage_products_helper.rb
| 1 | 1 | module ManageProductsHelper |
| 2 | + | |
| 3 | + def remote_function_to_update_categories_selection(container_id, options = {}) | |
| 4 | + remote_function({ | |
| 5 | + :update => container_id, | |
| 6 | + :url => { :action => "categories_for_selection" }, | |
| 7 | + :loading => "loading('hierarchy_navigation', '#{ _('loading…') }'); loading('#{container_id}', ' ')", | |
| 8 | + :complete => "loading_done('hierarchy_navigation'); loading_done('#{container_id}')" | |
| 9 | + }.merge(options)) | |
| 10 | + end | |
| 11 | + | |
| 12 | + def hierarchy_category_item(category, make_links, title = nil) | |
| 13 | + title ||= category.name | |
| 14 | + if make_links | |
| 15 | + link_to(title, '#', | |
| 16 | + :title => title, | |
| 17 | + :onclick => remote_function_to_update_categories_selection("categories_container_level#{ category.level + 1 }", | |
| 18 | + :with => "'category_id=#{ category.id }'" | |
| 19 | + ) | |
| 20 | + ) | |
| 21 | + else | |
| 22 | + title | |
| 23 | + end | |
| 24 | + end | |
| 25 | + | |
| 26 | + def hierarchy_category_navigation(current_category, options = {}) | |
| 27 | + hierarchy = [] | |
| 28 | + if current_category | |
| 29 | + count_chars = 0 | |
| 30 | + unless options[:hide_current_category] | |
| 31 | + hierarchy << current_category.name | |
| 32 | + count_chars += current_category.name.length | |
| 33 | + end | |
| 34 | + ancestors = current_category.ancestors | |
| 35 | + toplevel = ancestors.pop | |
| 36 | + if toplevel | |
| 37 | + count_chars += toplevel.name.length | |
| 38 | + end | |
| 39 | + ancestors.each do |category| | |
| 40 | + if count_chars > 55 | |
| 41 | + hierarchy << hierarchy_category_item(category, options[:make_links], '( … )') | |
| 42 | + break | |
| 43 | + else | |
| 44 | + hierarchy << hierarchy_category_item(category, options[:make_links]) | |
| 45 | + end | |
| 46 | + count_chars += category.name.length | |
| 47 | + end | |
| 48 | + if toplevel | |
| 49 | + hierarchy << hierarchy_category_item(toplevel, options[:make_links]) | |
| 50 | + end | |
| 51 | + end | |
| 52 | + hierarchy.reverse.join(options[:separator] || ' → ') | |
| 53 | + end | |
| 54 | + | |
| 55 | + def options_for_select_categories(categories, selected = nil) | |
| 56 | + categories.sort_by{|cat| cat.name.transliterate}.map do |category| | |
| 57 | + selected_attribute = selected.nil? ? '' : (category == selected ? "selected='selected'" : '') | |
| 58 | + "<option value='#{category.id}' title='#{category.name}' #{selected_attribute}>#{truncate(category.name, 33) + (category.leaf? ? '': ' »')}</option>" | |
| 59 | + end.join("\n") | |
| 60 | + end | |
| 61 | + | |
| 62 | + def build_selects_for_ancestors(ancestors, current_category) | |
| 63 | + current_ancestor = ancestors.shift | |
| 64 | + if current_ancestor.nil? | |
| 65 | + select_for_new_category(current_category.children, current_category.level + 1) | |
| 66 | + else | |
| 67 | + content_tag('div', | |
| 68 | + select_tag('category_id', | |
| 69 | + options_for_select_categories(current_ancestor.siblings + [current_ancestor], current_ancestor), | |
| 70 | + :size => 10, | |
| 71 | + :onchange => remote_function_to_update_categories_selection("categories_container_level#{ current_ancestor.level + 1 }", :with => "'category_id=' + this.value") | |
| 72 | + ) + | |
| 73 | + build_selects_for_ancestors(ancestors, current_category), | |
| 74 | + :class => 'categories_container', | |
| 75 | + :id => "categories_container_level#{ current_ancestor.level }" | |
| 76 | + ) | |
| 77 | + end | |
| 78 | + end | |
| 79 | + | |
| 80 | + def selects_for_all_ancestors(current_category) | |
| 81 | + build_selects_for_ancestors(current_category.ancestors.reverse + [current_category], current_category) | |
| 82 | + end | |
| 83 | + | |
| 84 | + def select_for_new_category(categories, level) | |
| 85 | + content_tag('div', | |
| 86 | + render(:partial => 'categories_for_selection', :locals => { :categories => categories, :level => level }), | |
| 87 | + :class => 'categories_container', | |
| 88 | + :id => "categories_container_level#{ level }" | |
| 89 | + ) | |
| 90 | + end | |
| 91 | + | |
| 92 | + def categories_container(categories_selection_html, hierarchy_html = '') | |
| 93 | + hidden_field_tag('selected_category_id') + | |
| 94 | + content_tag('div', hierarchy_html, :id => 'hierarchy_navigation') + | |
| 95 | + content_tag('div', categories_selection_html, :id => 'categories_container_wrapper') | |
| 96 | + end | |
| 97 | + | |
| 98 | + def select_for_categories(categories, level = 0) | |
| 99 | + if categories.empty? | |
| 100 | + content_tag('div', '', :id => 'no_subcategories') | |
| 101 | + else | |
| 102 | + select_tag('category_id', | |
| 103 | + options_for_select_categories(categories), | |
| 104 | + :size => 10, | |
| 105 | + :onchange => remote_function_to_update_categories_selection("categories_container_level#{ level + 1 }", :with => "'category_id=' + this.value") | |
| 106 | + ) + | |
| 107 | + content_tag('div', '', :class => 'categories_container', :id => "categories_container_level#{ level + 1 }") | |
| 108 | + end | |
| 109 | + end | |
| 110 | + | |
| 111 | + def edit_link(label, url, html_options = {}) | |
| 112 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
| 113 | + link_to(label, url, html_options) | |
| 114 | + end | |
| 115 | + | |
| 116 | + def edit_product_link_to_remote(product, field, label, html_options = {}) | |
| 117 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
| 118 | + options = html_options.merge(:id => 'link-edit-product-' + field) | |
| 119 | + options[:class] = options[:class] ? options[:class] + ' link-to-remote' : 'link-to-remote' | |
| 120 | + | |
| 121 | + link_to_remote(label, | |
| 122 | + {:update => "product-#{field}", | |
| 123 | + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field }, | |
| 124 | + :method => :get, | |
| 125 | + :loading => "loading_for_button('#link-edit-product-#{field}')"}, | |
| 126 | + options) | |
| 127 | + end | |
| 128 | + | |
| 129 | + def edit_button(type, label, url, html_options = {}) | |
| 130 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
| 131 | + button(type, label, url, html_options) | |
| 132 | + end | |
| 133 | + | |
| 134 | + def edit_product_button_to_remote(product, field, label, html_options = {}) | |
| 135 | + the_class = 'button with-text icon-edit' | |
| 136 | + if html_options.has_key?(:class) | |
| 137 | + the_class << ' ' << html_options[:class] | |
| 138 | + end | |
| 139 | + edit_product_link_to_remote(product, field, label, html_options.merge(:class => the_class)) | |
| 140 | + end | |
| 141 | + | |
| 142 | + def edit_ui_button(label, url, html_options = {}) | |
| 143 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
| 144 | + ui_button(label, url, html_options) | |
| 145 | + end | |
| 146 | + | |
| 147 | + def edit_product_ui_button_to_remote(product, field, label, html_options = {}) | |
| 148 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
| 149 | + id = 'edit-product-remote-button-ui-' + field | |
| 150 | + options = html_options.merge(:id => id) | |
| 151 | + | |
| 152 | + ui_button_to_remote(label, | |
| 153 | + {:update => "product-#{field}", | |
| 154 | + :url => { :controller => 'manage_products', :action => "edit", :id => product.id, :field => field }, | |
| 155 | + :complete => "$('edit-product-button-ui-#{field}').hide()", | |
| 156 | + :method => :get, | |
| 157 | + :loading => "loading_for_button('##{id}')"}, | |
| 158 | + options) | |
| 159 | + end | |
| 160 | + | |
| 161 | + def cancel_edit_product_link(product, field, html_options = {}) | |
| 162 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
| 163 | + button_to_function(:cancel, _('Cancel'), nil, html_options) do |page| | |
| 164 | + page.replace_html "product-#{field}", :partial => "display_#{field}", :locals => {:product => product} | |
| 165 | + end | |
| 166 | + end | |
| 167 | + | |
| 168 | + def edit_product_category_link(product, html_options = {}) | |
| 169 | + return '' unless (user && user.has_permission?('manage_products', profile)) | |
| 170 | + options = html_options.merge(:id => 'link-edit-product-category') | |
| 171 | + link_to(_('Change category'), { :action => 'edit_category', :id => product.id}, options) | |
| 172 | + end | |
| 173 | + | |
| 174 | + def display_value(product) | |
| 175 | + price = product.price | |
| 176 | + return '' if price.blank? || price.zero? | |
| 177 | + discount = product.discount | |
| 178 | + if discount.blank? || discount.zero? | |
| 179 | + result = display_price(_('Price: '), price) | |
| 180 | + else | |
| 181 | + result = display_price_with_discount(price, product.price_with_discount) | |
| 182 | + end | |
| 183 | + content_tag('span', content_tag('span', result, :class => 'product-price'), :class => "#{product.available? ? '' : 'un'}available-product") | |
| 184 | + end | |
| 185 | + | |
| 186 | + def display_availability(product) | |
| 187 | + if !product.available? | |
| 188 | + ui_highlight(_('Product not available!')) | |
| 189 | + end | |
| 190 | + end | |
| 191 | + | |
| 192 | + def display_price(label, price) | |
| 193 | + content_tag('span', label, :class => 'field-name') + | |
| 194 | + content_tag('span', float_to_currency(price), :class => 'field-value') | |
| 195 | + end | |
| 196 | + | |
| 197 | + def display_price_with_discount(price, price_with_discount) | |
| 198 | + original_value = content_tag('span', display_price(_('List price: '), price), :class => 'list-price') | |
| 199 | + discount_value = content_tag('span', display_price(_('On sale: '), price_with_discount), :class => 'on-sale-price') | |
| 200 | + original_value + tag('br') + discount_value | |
| 201 | + end | |
| 202 | + | |
| 203 | + def display_qualifiers(product) | |
| 204 | + data = '' | |
| 205 | + product.product_qualifiers.each do |pq| | |
| 206 | + certified_by = '' | |
| 207 | + certifier = pq.certifier | |
| 208 | + if certifier | |
| 209 | + certifier_name = certifier.link.blank? ? certifier.name : link_to(certifier.name, certifier.link) | |
| 210 | + certified_by = _('certified by %s') % certifier_name | |
| 211 | + else | |
| 212 | + certified_by = _('(Self declared)') | |
| 213 | + end | |
| 214 | + data << content_tag('li', "✔ #{pq.qualifier.name} #{certified_by}", :class => 'product-qualifiers-item') | |
| 215 | + end | |
| 216 | + content_tag('ul', data, :id => 'product-qualifiers') | |
| 217 | + end | |
| 218 | + | |
| 219 | + def qualifiers_for_select | |
| 220 | + [[_('Select...'), nil]] + environment.qualifiers.map{ |c| [c.name, c.id] } | |
| 221 | + end | |
| 222 | + def certifiers_for_select(qualifier) | |
| 223 | + [[_('Self declared'), nil]] + qualifier.certifiers.map{ |c| [c.name, c.id] } | |
| 224 | + end | |
| 225 | + def select_qualifiers(product, selected = nil) | |
| 226 | + select_tag('selected_qualifier', options_for_select(qualifiers_for_select, selected), | |
| 227 | + :onchange => remote_function( | |
| 228 | + :url => {:action => 'certifiers_for_selection'}, | |
| 229 | + :with => "'id=' + value + '&certifier_area=' + jQuery(this).parent().next().attr('id')", | |
| 230 | + :before => "small_loading(jQuery(this).parent().next().attr('id'), ' ')" | |
| 231 | + ), | |
| 232 | + :id => nil | |
| 233 | + ) | |
| 234 | + end | |
| 235 | + def select_certifiers(qualifier, product = nil) | |
| 236 | + if qualifier | |
| 237 | + selected = product ? product.product_qualifiers.find_by_qualifier_id(qualifier.id).certifier_id : nil | |
| 238 | + select_tag("product[qualifiers_list][#{qualifier.id}]", options_for_select(certifiers_for_select(qualifier), selected)) | |
| 239 | + else | |
| 240 | + select_tag("product[qualifiers_list][nil]") | |
| 241 | + end | |
| 242 | + end | |
| 243 | + | |
| 244 | + def select_unit(object) | |
| 245 | + selected = object.unit.nil? ? '' : object.unit | |
| 246 | + select(object.class.name.downcase, 'unit', Product::UNITS.map{|unit| [_(unit[0]), unit[0]]}, {:selected => selected, :include_blank => _('Select the unit')}) | |
| 247 | + end | |
| 248 | + | |
| 249 | + def input_icon(input) | |
| 250 | + if input.is_from_solidarity_economy? | |
| 251 | + hint = _('Product from solidarity economy') | |
| 252 | + image_tag("/images/solidarity-economy.png", :class => 'solidatiry-economy-icon', :alt => hint, :title => hint) | |
| 253 | + end | |
| 254 | + end | |
| 255 | + | |
| 256 | + def display_price_by(unit) | |
| 257 | + selected_unit = content_tag('span', unit, :class => 'selected-unit') | |
| 258 | + content_tag('span', _('by') + ' ' + selected_unit, :class => 'price-by-unit') | |
| 259 | + end | |
| 260 | + | |
| 261 | + def label_amount_used(input) | |
| 262 | + product_unit = input.product.unit | |
| 263 | + if product_unit.blank? | |
| 264 | + _('Amount used in this product or service') | |
| 265 | + else | |
| 266 | + _('Amount used by %s of this product or service') % _(product_unit) | |
| 267 | + end | |
| 268 | + end | |
| 269 | + | |
| 270 | + def display_unit(input) | |
| 271 | + input_amount_used = content_tag('span', input.formatted_amount, :class => 'input-amount-used') | |
| 272 | + return input_amount_used if input.unit.blank? | |
| 273 | + units = Product::UNITS.find {|unit| unit[0] == input.unit} | |
| 274 | + n_('1 %{singular_unit}', '%{num} %{plural_unit}', input.amount_used.to_f) % { :num => input_amount_used, :singular_unit => content_tag('span', units[0], :class => 'input-unit'), :plural_unit => content_tag('span', units[1], :class => 'input-unit') } | |
| 275 | + end | |
| 2 | 276 | end | ... | ... |
app/helpers/profile_helper.rb
| ... | ... | @@ -15,9 +15,12 @@ module ProfileHelper |
| 15 | 15 | end |
| 16 | 16 | end |
| 17 | 17 | |
| 18 | - def pagination_links(collection, options={}) | |
| 19 | - options = {:prev_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options) | |
| 20 | - will_paginate(collection, options) | |
| 21 | - end | |
| 18 | + def render_tabs(tabs) | |
| 19 | + titles = tabs.inject(''){ |result, tab| result << content_tag(:li, link_to(tab[:title], '#'+tab[:id]), :class => 'tab') } | |
| 20 | + contents = tabs.inject(''){ |result, tab| result << content_tag(:div, tab[:content], :id => tab[:id]) } | |
| 22 | 21 | |
| 22 | + content_tag :div, :class => 'ui-tabs' do | |
| 23 | + content_tag(:ul, titles) + contents | |
| 24 | + end | |
| 25 | + end | |
| 23 | 26 | end | ... | ... |
app/helpers/search_helper.rb
| ... | ... | @@ -98,11 +98,6 @@ module SearchHelper |
| 98 | 98 | ), :class => 'profile-info') |
| 99 | 99 | end |
| 100 | 100 | |
| 101 | - def pagination_links(collection, options={}) | |
| 102 | - options = {:prev_label => '« ' + _('Previous'), :next_label => _('Next') + ' »'}.merge(options) | |
| 103 | - will_paginate(collection, options) | |
| 104 | - end | |
| 105 | - | |
| 106 | 101 | def product_categories_menu(asset, product_category, object_ids = nil) |
| 107 | 102 | cats = ProductCategory.menu_categories(@product_category, environment) |
| 108 | 103 | cats += cats.select { |c| c.children_count > 0 }.map(&:children).flatten | ... | ... |
| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | +class ActionTrackerNotification < ActiveRecord::Base | |
| 2 | + | |
| 3 | + belongs_to :profile | |
| 4 | + belongs_to :action_tracker, :class_name => 'ActionTracker::Record', :foreign_key => 'action_tracker_id' | |
| 5 | + | |
| 6 | + validates_presence_of :profile_id, :action_tracker_id | |
| 7 | + validates_uniqueness_of :action_tracker_id, :scope => :profile_id | |
| 8 | + | |
| 9 | +end | |
| 10 | + | |
| 11 | +ActionTracker::Record.has_many :action_tracker_notifications, :class_name => 'ActionTrackerNotification', :foreign_key => 'action_tracker_id', :dependent => :destroy | ... | ... |
app/models/add_friend.rb
| 1 | 1 | class AddFriend < Task |
| 2 | 2 | |
| 3 | - acts_as_having_settings :group_for_person, :group_for_friend, :field => :data | |
| 3 | + settings_items :group_for_person, :group_for_friend | |
| 4 | 4 | |
| 5 | 5 | validates_presence_of :requestor_id, :target_id |
| 6 | 6 | |
| 7 | 7 | validates_uniqueness_of :target_id, :scope => [ :requestor_id ] |
| 8 | 8 | |
| 9 | - validates_length_of :group_for_person, :group_for_friend, :maximum => 150, :allow_nil => true | |
| 9 | + validates_length_of :group_for_person, :group_for_friend, :maximum => 150, :allow_nil => true | |
| 10 | 10 | |
| 11 | 11 | alias :person :requestor |
| 12 | 12 | alias :person= :requestor= |
| ... | ... | @@ -15,21 +15,37 @@ class AddFriend < Task |
| 15 | 15 | alias :friend= :target= |
| 16 | 16 | |
| 17 | 17 | def perform |
| 18 | - requestor.add_friend(target, group_for_person) | |
| 19 | 18 | target.add_friend(requestor, group_for_friend) |
| 20 | - end | |
| 21 | - | |
| 22 | - def description | |
| 23 | - _('%s wants to be your friend.') % requestor.name | |
| 19 | + requestor.add_friend(target, group_for_person) | |
| 24 | 20 | end |
| 25 | 21 | |
| 26 | 22 | def permission |
| 27 | 23 | :manage_friends |
| 28 | 24 | end |
| 29 | 25 | |
| 26 | + def target_notification_description | |
| 27 | + _('%{requestor} wants to be your friend.') % {:requestor => requestor.name} | |
| 28 | + end | |
| 29 | + | |
| 30 | 30 | def target_notification_message |
| 31 | - description + "\n\n" + | |
| 31 | + target_notification_description + "\n\n" + | |
| 32 | 32 | _('You need to login to %{system} in order to accept %{requestor} as your friend.') % { :system => target.environment.name, :requestor => requestor.name } |
| 33 | 33 | end |
| 34 | 34 | |
| 35 | + def title | |
| 36 | + _("New friend") | |
| 37 | + end | |
| 38 | + | |
| 39 | + def information | |
| 40 | + {:message => _('%{requestor} wants to be your friend.') } | |
| 41 | + end | |
| 42 | + | |
| 43 | + def accept_details | |
| 44 | + true | |
| 45 | + end | |
| 46 | + | |
| 47 | + def icon | |
| 48 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
| 49 | + end | |
| 50 | + | |
| 35 | 51 | end | ... | ... |
app/models/add_member.rb
| ... | ... | @@ -8,23 +8,39 @@ class AddMember < Task |
| 8 | 8 | alias :organization :target |
| 9 | 9 | alias :organization= :target= |
| 10 | 10 | |
| 11 | - acts_as_having_settings :roles, :field => :data | |
| 11 | + settings_items :roles | |
| 12 | 12 | |
| 13 | 13 | def perform |
| 14 | 14 | self.roles ||= [Profile::Roles.member(organization.environment.id).id] |
| 15 | 15 | target.affiliate(requestor, self.roles.select{|r| !r.to_i.zero? }.map{|i| Role.find(i)}) |
| 16 | 16 | end |
| 17 | 17 | |
| 18 | - def description | |
| 19 | - _('%s wants to be a member of "%s".') % [requestor.name, organization.name] | |
| 18 | + def title | |
| 19 | + _("New member") | |
| 20 | + end | |
| 21 | + | |
| 22 | + def information | |
| 23 | + {:message => _('%{requestor} wants to be a member of this community.')} | |
| 24 | + end | |
| 25 | + | |
| 26 | + def accept_details | |
| 27 | + true | |
| 28 | + end | |
| 29 | + | |
| 30 | + def icon | |
| 31 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
| 20 | 32 | end |
| 21 | 33 | |
| 22 | 34 | def permission |
| 23 | 35 | :manage_memberships |
| 24 | 36 | end |
| 25 | 37 | |
| 38 | + def target_notification_description | |
| 39 | + _('%{requestor} wants to be a member of this community.') % {:requestor => requestor.name} | |
| 40 | + end | |
| 41 | + | |
| 26 | 42 | def target_notification_message |
| 27 | - description + "\n\n" + | |
| 43 | + target_notification_description + "\n\n" + | |
| 28 | 44 | _('You will need login to %{system} in order to accept or reject %{requestor} as a member of %{organization}.') % { :system => target.environment.name, :requestor => requestor.name, :organization => organization.name } |
| 29 | 45 | end |
| 30 | 46 | ... | ... |
app/models/approve_article.rb
| 1 | 1 | class ApproveArticle < Task |
| 2 | - serialize :data, Hash | |
| 3 | - | |
| 4 | 2 | validates_presence_of :requestor_id, :target_id |
| 5 | 3 | |
| 6 | - def description | |
| 7 | - _('%{author} wants to publish "%{article}" on %{community}') % { :author => requestor.name, :article => article_title, :community => target.name } | |
| 8 | - end | |
| 9 | - | |
| 10 | 4 | def article_title |
| 11 | 5 | article ? article.title : _('(The original text was removed)') |
| 12 | 6 | end |
| 13 | 7 | |
| 14 | - def data | |
| 15 | - self[:data] ||= {} | |
| 16 | - end | |
| 17 | - | |
| 18 | 8 | def article |
| 19 | 9 | Article.find_by_id data[:article_id] |
| 20 | 10 | end |
| ... | ... | @@ -24,53 +14,97 @@ class ApproveArticle < Task |
| 24 | 14 | end |
| 25 | 15 | |
| 26 | 16 | def name |
| 27 | - data[:name] | |
| 17 | + data[:name].blank? ? (article ? article.name : _("Article removed.")) : data[:name] | |
| 28 | 18 | end |
| 29 | 19 | |
| 30 | 20 | def name= value |
| 31 | 21 | data[:name] = value |
| 32 | 22 | end |
| 33 | 23 | |
| 34 | - def closing_statment | |
| 35 | - data[:closing_statment] | |
| 24 | + settings_items :closing_statment, :article_parent_id, :highlighted | |
| 25 | + | |
| 26 | + def article_parent | |
| 27 | + Article.find_by_id article_parent_id.to_i | |
| 36 | 28 | end |
| 37 | - | |
| 38 | - def closing_statment= value | |
| 39 | - data[:closing_statment] = value | |
| 29 | + | |
| 30 | + def article_parent= value | |
| 31 | + self.article_parent_id = value.id | |
| 40 | 32 | end |
| 41 | 33 | |
| 42 | - def article_parent_id= value | |
| 43 | - data[:parent_id] = value | |
| 34 | + def abstract= value | |
| 35 | + data[:abstract] = value | |
| 44 | 36 | end |
| 45 | 37 | |
| 46 | - def article_parent_id | |
| 47 | - data[:parent_id] | |
| 38 | + def abstract | |
| 39 | + data[:abstract].blank? ? (article ? article.abstract : '') : data[:abstract] | |
| 48 | 40 | end |
| 49 | 41 | |
| 50 | - def article_parent | |
| 51 | - Article.find_by_id article_parent_id.to_i | |
| 42 | + def body= value | |
| 43 | + data[:body] = value | |
| 52 | 44 | end |
| 53 | 45 | |
| 54 | - def article_parent= value | |
| 55 | - self.article_parent_id = value.id | |
| 46 | + def body | |
| 47 | + data[:body].blank? ? (article ? article.body : "") : data[:body] | |
| 56 | 48 | end |
| 57 | 49 | |
| 58 | - def highlighted= value | |
| 59 | - data[:highlighted] = value | |
| 50 | + def perform | |
| 51 | + article.copy!(:name => name, :abstract => abstract, :body => body, :profile => target, :reference_article => article, :parent => article_parent, :highlighted => highlighted, :source => article.source) | |
| 60 | 52 | end |
| 61 | 53 | |
| 62 | - def highlighted | |
| 63 | - data[:highlighted] | |
| 54 | + def title | |
| 55 | + _("New article") | |
| 64 | 56 | end |
| 65 | 57 | |
| 66 | - def perform | |
| 67 | - PublishedArticle.create!(:name => name, :profile => target, :reference_article => article, :parent => article_parent, :highlighted => highlighted) | |
| 58 | + def icon | |
| 59 | + result = {:type => :defined_image, :src => '/images/icons-app/article-minor.png', :name => name} | |
| 60 | + result.merge({:url => article.url}) if article | |
| 61 | + return result | |
| 62 | + end | |
| 63 | + | |
| 64 | + def linked_subject | |
| 65 | + {:text => name, :url => article.url} if article | |
| 66 | + end | |
| 67 | + | |
| 68 | + def information | |
| 69 | + if article | |
| 70 | + {:message => _('%{requestor} wants to publish the article: %{linked_subject}.')} | |
| 71 | + else | |
| 72 | + {:message => _("The article was removed.")} | |
| 73 | + end | |
| 74 | + end | |
| 75 | + | |
| 76 | + def accept_details | |
| 77 | + true | |
| 78 | + end | |
| 79 | + | |
| 80 | + def default_decision | |
| 81 | + if article | |
| 82 | + 'skip' | |
| 83 | + else | |
| 84 | + 'reject' | |
| 85 | + end | |
| 86 | + end | |
| 87 | + | |
| 88 | + def accept_disabled? | |
| 89 | + article.blank? | |
| 90 | + end | |
| 91 | + | |
| 92 | + def target_notification_description | |
| 93 | + _('%{requestor} wants to publish the article: %{article}.') % {:requestor => requestor.name, :article => article.name} | |
| 68 | 94 | end |
| 69 | 95 | |
| 70 | 96 | def target_notification_message |
| 71 | 97 | return nil if target.organization? && !target.moderated_articles? |
| 72 | - description + "\n\n" + | |
| 98 | + target_notification_description + "\n\n" + | |
| 73 | 99 | _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name } |
| 74 | 100 | end |
| 75 | 101 | |
| 102 | + def task_finished_message | |
| 103 | + if !closing_statment.blank? | |
| 104 | + _("Your request for publishing the article \"%{article}\" was approved. Here is the comment left by the admin who approved your article:\n\n%{comment} ") % {:article => name, :comment => closing_statment} | |
| 105 | + else | |
| 106 | + _('Your request for publishing the article "%{article}" was approved.') % {:article => name} | |
| 107 | + end | |
| 108 | + end | |
| 109 | + | |
| 76 | 110 | end | ... | ... |
app/models/article.rb
| 1 | +require 'hpricot' | |
| 2 | + | |
| 1 | 3 | class Article < ActiveRecord::Base |
| 2 | 4 | |
| 5 | + track_actions :create_article, :after_create, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && !a.image? }, :custom_target => :action_tracker_target | |
| 6 | + track_actions :update_article, :before_update, :keep_params => [:name, :url], :if => Proc.new { |a| a.is_trackable? && (a.body_changed? || a.name_changed?) }, :custom_target => :action_tracker_target | |
| 7 | + track_actions :remove_article, :before_destroy, :keep_params => [:name], :if => Proc.new { |a| a.is_trackable? }, :custom_target => :action_tracker_target | |
| 8 | + | |
| 3 | 9 | # xss_terminate plugin can't sanitize array fields |
| 4 | 10 | before_save :sanitize_tag_list |
| 5 | 11 | |
| ... | ... | @@ -19,14 +25,25 @@ class Article < ActiveRecord::Base |
| 19 | 25 | acts_as_having_settings :field => :setting |
| 20 | 26 | |
| 21 | 27 | settings_items :display_hits, :type => :boolean, :default => true |
| 28 | + settings_items :author_name, :type => :string, :default => "" | |
| 22 | 29 | |
| 23 | 30 | belongs_to :reference_article, :class_name => "Article", :foreign_key => 'reference_article_id' |
| 24 | 31 | |
| 32 | + has_many :translations, :class_name => 'Article', :foreign_key => :translation_of_id | |
| 33 | + belongs_to :translation_of, :class_name => 'Article', :foreign_key => :translation_of_id | |
| 34 | + before_destroy :rotate_translations | |
| 35 | + | |
| 25 | 36 | before_create do |article| |
| 26 | 37 | article.published_at = article.created_at if article.published_at.nil? |
| 38 | + if article.reference_article && !article.parent | |
| 39 | + parent = article.reference_article.parent | |
| 40 | + if parent && parent.blog? && article.profile.has_blog? | |
| 41 | + article.parent = article.profile.blog | |
| 42 | + end | |
| 43 | + end | |
| 27 | 44 | end |
| 28 | 45 | |
| 29 | - xss_terminate :only => [ :name ], :on => 'validation' | |
| 46 | + xss_terminate :only => [ :name ], :on => 'validation', :with => 'white_list' | |
| 30 | 47 | |
| 31 | 48 | named_scope :in_category, lambda { |category| |
| 32 | 49 | {:include => 'categories', :conditions => { 'categories.id' => category.id }} |
| ... | ... | @@ -38,20 +55,17 @@ class Article < ActiveRecord::Base |
| 38 | 55 | ] |
| 39 | 56 | }} |
| 40 | 57 | |
| 41 | - def self.first_day_of_month(date) | |
| 42 | - date ||= Date.today | |
| 43 | - Date.new(date.year, date.month, 1) | |
| 44 | - end | |
| 45 | - | |
| 46 | - def self.last_day_of_month(date) | |
| 47 | - date ||= Date.today | |
| 48 | - date >>= 1 | |
| 49 | - Date.new(date.year, date.month, 1) - 1.day | |
| 50 | - end | |
| 51 | - | |
| 52 | 58 | URL_FORMAT = /\A(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?\Z/ix |
| 53 | 59 | |
| 54 | 60 | validates_format_of :external_link, :with => URL_FORMAT, :if => lambda { |article| !article.external_link.blank? } |
| 61 | + validate :known_language | |
| 62 | + validate :used_translation | |
| 63 | + validate :native_translation_must_have_language | |
| 64 | + validate :translation_must_have_language | |
| 65 | + | |
| 66 | + def is_trackable? | |
| 67 | + self.published? && self.notifiable? && self.advertise? | |
| 68 | + end | |
| 55 | 69 | |
| 56 | 70 | def external_link=(link) |
| 57 | 71 | if !link.blank? && link !~ /^[a-z]+:\/\//i |
| ... | ... | @@ -60,6 +74,9 @@ class Article < ActiveRecord::Base |
| 60 | 74 | self[:external_link] = link |
| 61 | 75 | end |
| 62 | 76 | |
| 77 | + def action_tracker_target | |
| 78 | + self.profile | |
| 79 | + end | |
| 63 | 80 | |
| 64 | 81 | def self.human_attribute_name(attrib) |
| 65 | 82 | case attrib.to_sym |
| ... | ... | @@ -120,9 +137,9 @@ class Article < ActiveRecord::Base |
| 120 | 137 | |
| 121 | 138 | # retrieves all articles belonging to the given +profile+ that are not |
| 122 | 139 | # sub-articles of any other article. |
| 123 | - def self.top_level_for(profile) | |
| 124 | - self.find(:all, :conditions => [ 'parent_id is null and profile_id = ?', profile.id ]) | |
| 125 | - end | |
| 140 | + named_scope :top_level_for, lambda { |profile| | |
| 141 | + {:conditions => [ 'parent_id is null and profile_id = ?', profile.id ]} | |
| 142 | + } | |
| 126 | 143 | |
| 127 | 144 | # retrieves the latest +limit+ articles, sorted from the most recent to the |
| 128 | 145 | # oldest. |
| ... | ... | @@ -182,7 +199,7 @@ class Article < ActiveRecord::Base |
| 182 | 199 | # to return their specific icons. |
| 183 | 200 | # |
| 184 | 201 | # FIXME use mime_type and generate this name dinamically |
| 185 | - def icon_name | |
| 202 | + def self.icon_name(article = nil) | |
| 186 | 203 | 'text-html' |
| 187 | 204 | end |
| 188 | 205 | |
| ... | ... | @@ -206,10 +223,24 @@ class Article < ActiveRecord::Base |
| 206 | 223 | name |
| 207 | 224 | end |
| 208 | 225 | |
| 226 | + include ActionView::Helpers::TextHelper | |
| 227 | + def short_title | |
| 228 | + truncate self.title, 15, '...' | |
| 229 | + end | |
| 230 | + | |
| 209 | 231 | def belongs_to_blog? |
| 210 | 232 | self.parent and self.parent.blog? |
| 211 | 233 | end |
| 212 | 234 | |
| 235 | + def info_from_last_update | |
| 236 | + last_comment = comments.last | |
| 237 | + if last_comment | |
| 238 | + {:date => last_comment.created_at, :author_name => last_comment.author_name, :author_url => last_comment.author_url} | |
| 239 | + else | |
| 240 | + {:date => updated_at, :author_name => author.name, :author_url => author.url} | |
| 241 | + end | |
| 242 | + end | |
| 243 | + | |
| 213 | 244 | def url |
| 214 | 245 | @url ||= self.profile.url.merge(:page => path.split('/')) |
| 215 | 246 | end |
| ... | ... | @@ -230,6 +261,73 @@ class Article < ActiveRecord::Base |
| 230 | 261 | false |
| 231 | 262 | end |
| 232 | 263 | |
| 264 | + def forum? | |
| 265 | + false | |
| 266 | + end | |
| 267 | + | |
| 268 | + def has_posts? | |
| 269 | + false | |
| 270 | + end | |
| 271 | + | |
| 272 | + named_scope :native_translations, :conditions => { :translation_of_id => nil } | |
| 273 | + | |
| 274 | + def translatable? | |
| 275 | + false | |
| 276 | + end | |
| 277 | + | |
| 278 | + def native_translation | |
| 279 | + self.translation_of.nil? ? self : self.translation_of | |
| 280 | + end | |
| 281 | + | |
| 282 | + def possible_translations | |
| 283 | + possibilities = Noosfero.locales.keys - self.native_translation.translations(:select => :language).map(&:language) - [self.native_translation.language] | |
| 284 | + possibilities << self.language unless self.language_changed? | |
| 285 | + possibilities | |
| 286 | + end | |
| 287 | + | |
| 288 | + def known_language | |
| 289 | + unless self.language.blank? | |
| 290 | + errors.add(:language, N_('Language not supported by Noosfero')) unless Noosfero.locales.key?(self.language) | |
| 291 | + end | |
| 292 | + end | |
| 293 | + | |
| 294 | + def used_translation | |
| 295 | + unless self.language.blank? or self.translation_of.nil? | |
| 296 | + errors.add(:language, N_('Language is already used')) unless self.possible_translations.include?(self.language) | |
| 297 | + end | |
| 298 | + end | |
| 299 | + | |
| 300 | + def translation_must_have_language | |
| 301 | + unless self.translation_of.nil? | |
| 302 | + errors.add(:language, N_('Language must be choosen')) if self.language.blank? | |
| 303 | + end | |
| 304 | + end | |
| 305 | + | |
| 306 | + def native_translation_must_have_language | |
| 307 | + unless self.translation_of.nil? | |
| 308 | + errors.add_to_base(N_('A language must be choosen for the native article')) if self.translation_of.language.blank? | |
| 309 | + end | |
| 310 | + end | |
| 311 | + | |
| 312 | + def rotate_translations | |
| 313 | + unless self.translations.empty? | |
| 314 | + rotate = self.translations | |
| 315 | + root = rotate.shift | |
| 316 | + root.update_attribute(:translation_of_id, nil) | |
| 317 | + root.translations = rotate | |
| 318 | + end | |
| 319 | + end | |
| 320 | + | |
| 321 | + def get_translation_to(locale) | |
| 322 | + if self.language.nil? || self.language == locale | |
| 323 | + self | |
| 324 | + elsif self.native_translation.language == locale | |
| 325 | + self.native_translation | |
| 326 | + else | |
| 327 | + self.native_translation.translations.first(:conditions => { :language => locale }) || self | |
| 328 | + end | |
| 329 | + end | |
| 330 | + | |
| 233 | 331 | def published? |
| 234 | 332 | if self.published |
| 235 | 333 | if self.parent && !self.parent.published? |
| ... | ... | @@ -241,22 +339,33 @@ class Article < ActiveRecord::Base |
| 241 | 339 | end |
| 242 | 340 | end |
| 243 | 341 | |
| 244 | - named_scope :published, :conditions => { :published => true } | |
| 245 | - named_scope :folders, :conditions => { :type => ['Folder', 'Blog'] } | |
| 342 | + named_scope :published, :conditions => { :published => true } | |
| 343 | + named_scope :folders, :conditions => { :type => ['Folder', 'Blog', 'Forum', 'Gallery'] } | |
| 344 | + named_scope :galleries, :conditions => { :type => 'Gallery' } | |
| 345 | + named_scope :images, :conditions => { :is_image => true } | |
| 346 | + | |
| 347 | + def self.display_filter(user, profile) | |
| 348 | + return {:conditions => ['published = ?', true]} if !user | |
| 349 | + {:conditions => [" articles.published = ? OR | |
| 350 | + articles.last_changed_by_id = ? OR | |
| 351 | + articles.profile_id = ? OR | |
| 352 | + ?", | |
| 353 | + true, user.id, user.id, user.has_permission?(:view_private_content, profile)] } | |
| 354 | + end | |
| 246 | 355 | |
| 247 | 356 | def display_unpublished_article_to?(user) |
| 248 | - self.author == user || allow_view_private_content?(user) || user == self.profile || | |
| 249 | - user.is_admin?(self.profile.environment) || user.is_admin?(self.profile) | |
| 357 | + user == author || allow_view_private_content?(user) || user == profile || | |
| 358 | + user.is_admin?(profile.environment) || user.is_admin?(profile) | |
| 250 | 359 | end |
| 251 | 360 | |
| 252 | - def display_to?(user) | |
| 253 | - if self.published? | |
| 254 | - self.profile.display_info_to?(user) | |
| 361 | + def display_to?(user = nil) | |
| 362 | + if published? | |
| 363 | + profile.display_info_to?(user) | |
| 255 | 364 | else |
| 256 | - if user.nil? | |
| 365 | + if !user | |
| 257 | 366 | false |
| 258 | 367 | else |
| 259 | - self.display_unpublished_article_to?(user) | |
| 368 | + display_unpublished_article_to?(user) | |
| 260 | 369 | end |
| 261 | 370 | end |
| 262 | 371 | end |
| ... | ... | @@ -286,12 +395,18 @@ class Article < ActiveRecord::Base |
| 286 | 395 | end |
| 287 | 396 | |
| 288 | 397 | |
| 289 | - def copy(options) | |
| 398 | + def copy(options = {}) | |
| 290 | 399 | attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) } |
| 291 | 400 | attrs.merge!(options) |
| 292 | 401 | self.class.create(attrs) |
| 293 | 402 | end |
| 294 | 403 | |
| 404 | + def copy!(options = {}) | |
| 405 | + attrs = attributes.reject! { |key, value| ATTRIBUTES_NOT_COPIED.include?(key.to_sym) } | |
| 406 | + attrs.merge!(options) | |
| 407 | + self.class.create!(attrs) | |
| 408 | + end | |
| 409 | + | |
| 295 | 410 | ATTRIBUTES_NOT_COPIED = [ |
| 296 | 411 | :id, |
| 297 | 412 | :profile_id, |
| ... | ... | @@ -329,13 +444,28 @@ class Article < ActiveRecord::Base |
| 329 | 444 | false |
| 330 | 445 | end |
| 331 | 446 | |
| 332 | - def display_as_gallery? | |
| 447 | + def event? | |
| 448 | + false | |
| 449 | + end | |
| 450 | + | |
| 451 | + def gallery? | |
| 452 | + false | |
| 453 | + end | |
| 454 | + | |
| 455 | + def tiny_mce? | |
| 333 | 456 | false |
| 334 | 457 | end |
| 335 | 458 | |
| 336 | 459 | def author |
| 337 | - last_changed_by || | |
| 338 | - profile | |
| 460 | + if reference_article | |
| 461 | + reference_article.author | |
| 462 | + else | |
| 463 | + last_changed_by || profile | |
| 464 | + end | |
| 465 | + end | |
| 466 | + | |
| 467 | + def author_name | |
| 468 | + setting[:author_name].blank? ? author.name : setting[:author_name] | |
| 339 | 469 | end |
| 340 | 470 | |
| 341 | 471 | alias :active_record_cache_key :cache_key |
| ... | ... | @@ -348,8 +478,16 @@ class Article < ActiveRecord::Base |
| 348 | 478 | end |
| 349 | 479 | |
| 350 | 480 | def first_paragraph |
| 351 | - to_html =~ /(.*<\/p>)/ | |
| 352 | - $1 || '' | |
| 481 | + paragraphs = Hpricot(to_html).search('p') | |
| 482 | + paragraphs.empty? ? '' : paragraphs.first.to_html | |
| 483 | + end | |
| 484 | + | |
| 485 | + def lead | |
| 486 | + abstract.blank? ? first_paragraph : abstract | |
| 487 | + end | |
| 488 | + | |
| 489 | + def short_lead | |
| 490 | + truncate sanitize_html(self.lead), 170, '...' | |
| 353 | 491 | end |
| 354 | 492 | |
| 355 | 493 | def creator |
| ... | ... | @@ -357,6 +495,10 @@ class Article < ActiveRecord::Base |
| 357 | 495 | creator_id && Profile.find(creator_id) |
| 358 | 496 | end |
| 359 | 497 | |
| 498 | + def notifiable? | |
| 499 | + false | |
| 500 | + end | |
| 501 | + | |
| 360 | 502 | private |
| 361 | 503 | |
| 362 | 504 | def sanitize_tag_list |
| ... | ... | @@ -368,4 +510,9 @@ class Article < ActiveRecord::Base |
| 368 | 510 | tag_name.gsub(/[<>]/, '') |
| 369 | 511 | end |
| 370 | 512 | |
| 513 | + def sanitize_html(text) | |
| 514 | + sanitizer = HTML::FullSanitizer.new | |
| 515 | + sanitizer.sanitize(text) | |
| 516 | + end | |
| 517 | + | |
| 371 | 518 | end | ... | ... |
app/models/block.rb
| ... | ... | @@ -19,12 +19,22 @@ class Block < ActiveRecord::Base |
| 19 | 19 | # may contain the following keys: |
| 20 | 20 | # |
| 21 | 21 | # * <tt>:article</tt>: the article being viewed currently |
| 22 | + # * <tt>:language</tt>: in which language the block will be displayed | |
| 22 | 23 | def visible?(context = nil) |
| 23 | 24 | if display == 'never' |
| 24 | 25 | return false |
| 25 | 26 | end |
| 26 | - if context && context[:article] && display == 'home_page_only' | |
| 27 | - return context[:article] == owner.home_page | |
| 27 | + if context | |
| 28 | + if language != 'all' && language != context[:locale] | |
| 29 | + return false | |
| 30 | + end | |
| 31 | + if display == 'home_page_only' | |
| 32 | + if context[:article] | |
| 33 | + return context[:article] == owner.home_page | |
| 34 | + else | |
| 35 | + return context[:request_path] == '/' | |
| 36 | + end | |
| 37 | + end | |
| 28 | 38 | end |
| 29 | 39 | true |
| 30 | 40 | end |
| ... | ... | @@ -37,6 +47,11 @@ class Block < ActiveRecord::Base |
| 37 | 47 | # homepage of its owner. |
| 38 | 48 | settings_items :display, :type => :string, :default => 'always' |
| 39 | 49 | |
| 50 | + # The block can be configured to be displayed in all languages or in just one language. It can assume any locale of the environment: | |
| 51 | + # | |
| 52 | + # * <tt>'all'</tt>: the block is always displayed | |
| 53 | + settings_items :language, :type => :string, :default => 'all' | |
| 54 | + | |
| 40 | 55 | # returns the description of the block, used when the user sees a list of |
| 41 | 56 | # blocks to choose one to include in the design. |
| 42 | 57 | # | ... | ... |
app/models/blog.rb
| 1 | 1 | class Blog < Folder |
| 2 | 2 | |
| 3 | - has_many :posts, :class_name => 'Article', :foreign_key => 'parent_id', :source => :children, :conditions => [ 'type != ?', 'RssFeed' ], :order => 'published_at DESC, id DESC' | |
| 4 | - | |
| 5 | - attr_accessor :feed_attrs | |
| 6 | - | |
| 7 | - after_create do |blog| | |
| 8 | - blog.children << RssFeed.new(:name => 'feed', :profile => blog.profile) | |
| 9 | - blog.feed = blog.feed_attrs | |
| 10 | - end | |
| 11 | - | |
| 12 | - settings_items :posts_per_page, :type => :integer, :default => 5 | |
| 3 | + acts_as_having_posts | |
| 13 | 4 | |
| 14 | 5 | def self.short_description |
| 15 | 6 | _('Blog') |
| ... | ... | @@ -35,21 +26,6 @@ class Blog < Folder |
| 35 | 26 | true |
| 36 | 27 | end |
| 37 | 28 | |
| 38 | - def feed | |
| 39 | - self.children.find(:first, :conditions => {:type => 'RssFeed'}) | |
| 40 | - end | |
| 41 | - | |
| 42 | - def feed=(attrs) | |
| 43 | - if attrs | |
| 44 | - if self.feed | |
| 45 | - self.feed.update_attributes(attrs) | |
| 46 | - else | |
| 47 | - self.feed_attrs = attrs | |
| 48 | - end | |
| 49 | - end | |
| 50 | - self.feed | |
| 51 | - end | |
| 52 | - | |
| 53 | 29 | has_one :external_feed, :foreign_key => 'blog_id', :dependent => :destroy |
| 54 | 30 | |
| 55 | 31 | attr_accessor :external_feed_data |
| ... | ... | @@ -78,17 +54,15 @@ class Blog < Folder |
| 78 | 54 | end |
| 79 | 55 | end |
| 80 | 56 | |
| 81 | - def name=(value) | |
| 82 | - self.set_name(value) | |
| 83 | - if self.slug.blank? | |
| 84 | - self.slug = self.name.to_slug | |
| 85 | - else | |
| 86 | - self.slug = self.slug.to_slug | |
| 87 | - end | |
| 57 | + def self.icon_name(article = nil) | |
| 58 | + 'blog' | |
| 88 | 59 | end |
| 89 | 60 | |
| 90 | 61 | settings_items :visualization_format, :type => :string, :default => 'full' |
| 91 | 62 | validates_inclusion_of :visualization_format, :in => [ 'full', 'short' ], :if => :visualization_format |
| 92 | 63 | |
| 64 | + settings_items :display_posts_in_current_language, :type => :boolean, :default => true | |
| 65 | + | |
| 66 | + alias :display_posts_in_current_language? :display_posts_in_current_language | |
| 93 | 67 | |
| 94 | 68 | end | ... | ... |
app/models/blog_archives_block.rb
| ... | ... | @@ -24,7 +24,7 @@ class BlogArchivesBlock < Block |
| 24 | 24 | owner_blog = self.blog |
| 25 | 25 | return nil unless owner_blog |
| 26 | 26 | results = '' |
| 27 | - owner_blog.posts.group_by {|i| i.published_at.year }.sort_by { |year,count| -year }.each do |year, results_by_year| | |
| 27 | + owner_blog.posts.native_translations.group_by {|i| i.published_at.year }.sort_by { |year,count| -year }.each do |year, results_by_year| | |
| 28 | 28 | results << content_tag('li', content_tag('strong', "#{year} (#{results_by_year.size})")) |
| 29 | 29 | results << "<ul class='#{year}-archive'>" |
| 30 | 30 | results_by_year.group_by{|i| [ ('%02d' % i.published_at.month()), gettext(MONTHS[i.published_at.month() - 1])]}.sort.reverse.each do |month, results_by_month| | ... | ... |
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | +class CategoriesBlock < Block | |
| 2 | + | |
| 3 | + CATEGORY_TYPES = { | |
| 4 | + _('Generic category') => nil, | |
| 5 | + _('Region') => 'Region', | |
| 6 | + _('Product') => 'ProductCategory' | |
| 7 | + } | |
| 8 | + | |
| 9 | + settings_items :category_types, :type => Array, :default => [] | |
| 10 | + | |
| 11 | + def self.description | |
| 12 | + _("Categories Menu") | |
| 13 | + end | |
| 14 | + | |
| 15 | + def default_title | |
| 16 | + _("Categories Menu") | |
| 17 | + end | |
| 18 | + | |
| 19 | + def help | |
| 20 | + _('This block presents the categories like a web site menu.') | |
| 21 | + end | |
| 22 | + | |
| 23 | + def available_category_types | |
| 24 | + CATEGORY_TYPES | |
| 25 | + end | |
| 26 | + | |
| 27 | + def selected_categories | |
| 28 | + Category.top_level_for(self.owner).from_types(self.category_types) | |
| 29 | + end | |
| 30 | + | |
| 31 | + def content | |
| 32 | + block = self | |
| 33 | + lambda do | |
| 34 | + render :file => 'blocks/categories', :locals => { :block => block } | |
| 35 | + end | |
| 36 | + end | |
| 37 | + | |
| 38 | +end | ... | ... |
app/models/category.rb
| ... | ... | @@ -9,9 +9,9 @@ class Category < ActiveRecord::Base |
| 9 | 9 | validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.') |
| 10 | 10 | |
| 11 | 11 | # Finds all top level categories for a given environment. |
| 12 | - def self.top_level_for(environment) | |
| 13 | - self.find(:all, :conditions => ['parent_id is null and environment_id = ?', environment.id ]) | |
| 14 | - end | |
| 12 | + named_scope :top_level_for, lambda { |environment| | |
| 13 | + {:conditions => ['parent_id is null and environment_id = ?', environment.id ]} | |
| 14 | + } | |
| 15 | 15 | |
| 16 | 16 | acts_as_filesystem |
| 17 | 17 | |
| ... | ... | @@ -30,6 +30,12 @@ class Category < ActiveRecord::Base |
| 30 | 30 | |
| 31 | 31 | acts_as_having_image |
| 32 | 32 | |
| 33 | + named_scope :from_types, lambda { |types| | |
| 34 | + types.select{ |t| t.blank? }.empty? ? | |
| 35 | + { :conditions => { :type => types } } : | |
| 36 | + { :conditions => [ "type IN (?) OR type IS NULL", types.reject{ |t| t.blank? } ] } | |
| 37 | + } | |
| 38 | + | |
| 33 | 39 | def recent_articles(limit = 10) |
| 34 | 40 | self.articles.recent(limit) |
| 35 | 41 | end |
| ... | ... | @@ -58,4 +64,9 @@ class Category < ActiveRecord::Base |
| 58 | 64 | results |
| 59 | 65 | end |
| 60 | 66 | |
| 67 | + def is_leaf_displayable_in_menu? | |
| 68 | + return false if self.display_in_menu == false | |
| 69 | + self.children.find(:all, :conditions => {:display_in_menu => true}).empty? | |
| 70 | + end | |
| 71 | + | |
| 61 | 72 | end | ... | ... |
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | +class Certifier < ActiveRecord::Base | |
| 2 | + | |
| 3 | + belongs_to :environment | |
| 4 | + | |
| 5 | + has_many :qualifier_certifiers | |
| 6 | + has_many :qualifiers, :through => :qualifier_certifiers | |
| 7 | + | |
| 8 | + validates_presence_of :environment_id | |
| 9 | + validates_presence_of :name | |
| 10 | + | |
| 11 | + def link | |
| 12 | + self[:link] || '' | |
| 13 | + end | |
| 14 | +end | ... | ... |
app/models/change_password.rb
| 1 | 1 | class ChangePassword < Task |
| 2 | 2 | |
| 3 | - serialize :data, Hash | |
| 4 | - def data | |
| 5 | - self[:data] ||= {} | |
| 6 | - end | |
| 7 | - | |
| 8 | 3 | attr_accessor :login, :email, :password, :password_confirmation, :environment_id |
| 9 | 4 | |
| 10 | 5 | def self.human_attribute_name(attrib) |
| ... | ... | @@ -45,7 +40,7 @@ class ChangePassword < Task |
| 45 | 40 | end |
| 46 | 41 | |
| 47 | 42 | before_validation_on_create do |change_password| |
| 48 | - change_password.requestor = Person.find_by_identifier(change_password.login) | |
| 43 | + change_password.requestor = Person.find_by_identifier_and_environment_id(change_password.login, change_password.environment_id) | |
| 49 | 44 | end |
| 50 | 45 | |
| 51 | 46 | ################################################### |
| ... | ... | @@ -56,9 +51,16 @@ class ChangePassword < Task |
| 56 | 51 | validates_presence_of :password_confirmation, :on => :update, :if => lambda { |change| change.status != Task::Status::CANCELLED } |
| 57 | 52 | validates_confirmation_of :password, :if => lambda { |change| change.status != Task::Status::CANCELLED } |
| 58 | 53 | |
| 59 | - def initialize(*args) | |
| 60 | - super(*args) | |
| 61 | - self[:data] = {} | |
| 54 | + def title | |
| 55 | + _("Change password") | |
| 56 | + end | |
| 57 | + | |
| 58 | + def information | |
| 59 | + {:message => _('%{requestor} wants to change its password.')} | |
| 60 | + end | |
| 61 | + | |
| 62 | + def icon | |
| 63 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
| 62 | 64 | end |
| 63 | 65 | |
| 64 | 66 | def perform |
| ... | ... | @@ -87,8 +89,8 @@ class ChangePassword < Task |
| 87 | 89 | end |
| 88 | 90 | end |
| 89 | 91 | |
| 90 | - def description | |
| 91 | - _('Password change request') | |
| 92 | + def environment | |
| 93 | + self.requestor.environment | |
| 92 | 94 | end |
| 93 | 95 | |
| 94 | 96 | end | ... | ... |
app/models/comment.rb
| 1 | 1 | class Comment < ActiveRecord::Base |
| 2 | - | |
| 2 | + | |
| 3 | + track_actions :leave_comment, :after_create, :keep_params => ["article.title", "article.url", "title", "url", "body"], :custom_target => :action_tracker_target | |
| 4 | + | |
| 3 | 5 | validates_presence_of :title, :body |
| 4 | 6 | belongs_to :article, :counter_cache => true |
| 5 | 7 | belongs_to :author, :class_name => 'Person', :foreign_key => 'author_id' |
| 8 | + has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy | |
| 9 | + belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' | |
| 6 | 10 | |
| 7 | 11 | # unauthenticated authors: |
| 8 | 12 | validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) |
| ... | ... | @@ -19,6 +23,10 @@ class Comment < ActiveRecord::Base |
| 19 | 23 | |
| 20 | 24 | xss_terminate :only => [ :body, :title, :name ], :on => 'validation' |
| 21 | 25 | |
| 26 | + def action_tracker_target | |
| 27 | + self.article.profile | |
| 28 | + end | |
| 29 | + | |
| 22 | 30 | def author_name |
| 23 | 31 | if author |
| 24 | 32 | author.short_name |
| ... | ... | @@ -35,16 +43,20 @@ class Comment < ActiveRecord::Base |
| 35 | 43 | author ? author.url : email |
| 36 | 44 | end |
| 37 | 45 | |
| 46 | + def author_url | |
| 47 | + author ? author.url : nil | |
| 48 | + end | |
| 49 | + | |
| 38 | 50 | def url |
| 39 | 51 | article.view_url.merge(:anchor => anchor) |
| 40 | 52 | end |
| 41 | 53 | |
| 42 | 54 | def message |
| 43 | - author_id ? _('(removed user)') : ('<br />' + _('(unauthenticated user)')) | |
| 55 | + author_id ? _('(removed user)') : _('(unauthenticated user)') | |
| 44 | 56 | end |
| 45 | 57 | |
| 46 | 58 | def removed_user_image |
| 47 | - '/images/icons-app/user_icon_size-minor.png' | |
| 59 | + '/images/icons-app/person-minor.png' | |
| 48 | 60 | end |
| 49 | 61 | |
| 50 | 62 | def anchor |
| ... | ... | @@ -62,18 +74,34 @@ class Comment < ActiveRecord::Base |
| 62 | 74 | end |
| 63 | 75 | |
| 64 | 76 | after_create do |comment| |
| 65 | - if comment.article.notify_comments? | |
| 77 | + if comment.article.notify_comments? && !comment.article.profile.notification_emails.empty? | |
| 66 | 78 | Comment::Notifier.deliver_mail(comment) |
| 67 | 79 | end |
| 68 | 80 | end |
| 69 | 81 | |
| 82 | + def replies | |
| 83 | + @replies || children | |
| 84 | + end | |
| 85 | + | |
| 86 | + def replies=(comments_list) | |
| 87 | + @replies = comments_list | |
| 88 | + end | |
| 89 | + | |
| 90 | + def self.as_thread | |
| 91 | + result = {} | |
| 92 | + root = [] | |
| 93 | + all.each do |c| | |
| 94 | + c.replies = [] | |
| 95 | + result[c.id] ||= c | |
| 96 | + c.reply_of_id.nil? ? root << c : result[c.reply_of_id].replies << c | |
| 97 | + end | |
| 98 | + root | |
| 99 | + end | |
| 100 | + | |
| 70 | 101 | class Notifier < ActionMailer::Base |
| 71 | 102 | def mail(comment) |
| 72 | 103 | profile = comment.article.profile |
| 73 | - email = profile.notification_emails | |
| 74 | - return unless email | |
| 75 | - recipients email | |
| 76 | - | |
| 104 | + recipients profile.notification_emails | |
| 77 | 105 | from "#{profile.environment.name} <#{profile.environment.contact_email}>" |
| 78 | 106 | subject _("[%s] you got a new comment!") % [profile.environment.name] |
| 79 | 107 | body :recipient => profile.nickname || profile.name, |
| ... | ... | @@ -81,6 +109,8 @@ class Comment < ActiveRecord::Base |
| 81 | 109 | :sender_link => comment.author_link, |
| 82 | 110 | :article_title => comment.article.name, |
| 83 | 111 | :comment_url => comment.url, |
| 112 | + :comment_title => comment.title, | |
| 113 | + :comment_body => comment.body, | |
| 84 | 114 | :environment => profile.environment.name, |
| 85 | 115 | :url => profile.environment.top_url |
| 86 | 116 | end | ... | ... |
app/models/communities_block.rb
| ... | ... | @@ -8,10 +8,6 @@ class CommunitiesBlock < ProfileListBlock |
| 8 | 8 | n__('{#} community', '{#} communities', profile_count) |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | - def profile_image_link_method | |
| 12 | - :community_image_link | |
| 13 | - end | |
| 14 | - | |
| 15 | 11 | def help |
| 16 | 12 | __('This block displays the communities in which the user is a member.') |
| 17 | 13 | end |
| ... | ... | @@ -25,35 +21,15 @@ class CommunitiesBlock < ProfileListBlock |
| 25 | 21 | end |
| 26 | 22 | when Environment |
| 27 | 23 | lambda do |
| 28 | - link_to s_('communities|View all'), :controller => 'search', :action => 'assets', :asset => 'communities' | |
| 24 | + link_to s_('communities|View all'), :controller => 'browse', :action => 'communities' | |
| 29 | 25 | end |
| 30 | 26 | else |
| 31 | 27 | '' |
| 32 | 28 | end |
| 33 | 29 | end |
| 34 | 30 | |
| 35 | - def profile_count | |
| 36 | - if owner.kind_of?(Environment) | |
| 37 | - owner.communities.count(:conditions => { :visible => true }) | |
| 38 | - else | |
| 39 | - owner.communities(:visible => true).count | |
| 40 | - end | |
| 41 | - end | |
| 42 | - | |
| 43 | - def profile_finder | |
| 44 | - @profile_finder ||= CommunitiesBlock::Finder.new(self) | |
| 45 | - end | |
| 46 | - | |
| 47 | - class Finder < ProfileListBlock::Finder | |
| 48 | - def ids | |
| 49 | - # FIXME when owner is an environment (i.e. listing communities globally | |
| 50 | - # this can become SLOW) | |
| 51 | - if block.owner.kind_of?(Environment) | |
| 52 | - block.owner.communities.all(:conditions => {:visible => true}, :limit => block.limit, :order => 'random()').map(&:id) | |
| 53 | - else | |
| 54 | - block.owner.communities(:visible => true).map(&:id) | |
| 55 | - end | |
| 56 | - end | |
| 31 | + def profiles | |
| 32 | + owner.communities | |
| 57 | 33 | end |
| 58 | 34 | |
| 59 | 35 | end | ... | ... |
app/models/community.rb
| ... | ... | @@ -67,4 +67,12 @@ class Community < Organization |
| 67 | 67 | def blocks_to_expire_cache |
| 68 | 68 | [MembersBlock] |
| 69 | 69 | end |
| 70 | + | |
| 71 | + def each_member(offset=0) | |
| 72 | + while member = self.members.first(:order => :id, :offset => offset) | |
| 73 | + yield member | |
| 74 | + offset = offset + 1 | |
| 75 | + end | |
| 76 | + end | |
| 77 | + | |
| 70 | 78 | end | ... | ... |
app/models/consumption.rb
| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | +class ContactList < ActiveRecord::Base | |
| 2 | + | |
| 3 | + serialize :list, Array | |
| 4 | + | |
| 5 | + def list | |
| 6 | + self[:list] || [] | |
| 7 | + end | |
| 8 | + | |
| 9 | + def data | |
| 10 | + if self.fetched | |
| 11 | + { "fetched" => true, "contact_list" => self.id, "error" => self.error_fetching } | |
| 12 | + else | |
| 13 | + {} | |
| 14 | + end | |
| 15 | + end | |
| 16 | + | |
| 17 | + def register_auth_error | |
| 18 | + msg = _('There was an error while authenticating. Did you enter correct login and password?') | |
| 19 | + self.update_attributes(:fetched => true, :error_fetching => msg) | |
| 20 | + end | |
| 21 | + | |
| 22 | + def register_error | |
| 23 | + msg = _('There was an error while looking for your contact list. Please, try again') | |
| 24 | + self.update_attributes(:fetched => true, :error_fetching => msg) | |
| 25 | + end | |
| 26 | + | |
| 27 | +end | ... | ... |
app/models/create_community.rb
| ... | ... | @@ -6,25 +6,11 @@ class CreateCommunity < Task |
| 6 | 6 | alias :environment :target |
| 7 | 7 | alias :environment= :target= |
| 8 | 8 | |
| 9 | - serialize :data, Hash | |
| 10 | - attr_protected :data | |
| 11 | - def data | |
| 12 | - self[:data] ||= Hash.new | |
| 13 | - end | |
| 14 | - | |
| 15 | 9 | acts_as_having_image |
| 16 | 10 | |
| 17 | 11 | DATA_FIELDS = Community.fields + ['name', 'closed'] |
| 18 | - | |
| 19 | 12 | DATA_FIELDS.each do |field| |
| 20 | - # getter | |
| 21 | - define_method(field) do | |
| 22 | - self.data[field.to_sym] | |
| 23 | - end | |
| 24 | - # setter | |
| 25 | - define_method("#{field}=") do |value| | |
| 26 | - self.data[field.to_sym] = value | |
| 27 | - end | |
| 13 | + settings_items field.to_sym | |
| 28 | 14 | end |
| 29 | 15 | |
| 30 | 16 | def validate |
| ... | ... | @@ -48,16 +34,30 @@ class CreateCommunity < Task |
| 48 | 34 | community.add_admin(self.requestor) |
| 49 | 35 | end |
| 50 | 36 | |
| 51 | - def description | |
| 52 | - _('%s wants to create community %s.') % [requestor.name, self.name] | |
| 37 | + def title | |
| 38 | + _("New community") | |
| 39 | + end | |
| 40 | + | |
| 41 | + def icon | |
| 42 | + src = image ? image.public_filename(:minor) : '/images/icons-app/community-minor.png' | |
| 43 | + {:type => :defined_image, :src => src, :name => name} | |
| 44 | + end | |
| 45 | + | |
| 46 | + def subject | |
| 47 | + name | |
| 53 | 48 | end |
| 54 | 49 | |
| 55 | - def closing_statement | |
| 56 | - data[:closing_statement] | |
| 50 | + def information | |
| 51 | + if description.blank? | |
| 52 | + { :message => _('%{requestor} wants to create community %{subject} with no description.') } | |
| 53 | + else | |
| 54 | + { :message => _('%{requestor} wants to create community %{subject} with this description:<p><em>%{description}</em></p>'), | |
| 55 | + :variables => {:description => description} } | |
| 56 | + end | |
| 57 | 57 | end |
| 58 | 58 | |
| 59 | - def closing_statement= value | |
| 60 | - data[:closing_statement] = value | |
| 59 | + def reject_details | |
| 60 | + true | |
| 61 | 61 | end |
| 62 | 62 | |
| 63 | 63 | # tells if this request was rejected |
| ... | ... | @@ -70,8 +70,11 @@ class CreateCommunity < Task |
| 70 | 70 | self.status == Task::Status::FINISHED |
| 71 | 71 | end |
| 72 | 72 | |
| 73 | + def target_notification_description | |
| 74 | + _('%{requestor} wants to create community %{subject}') % {:requestor => requestor.name, :subject => subject} | |
| 75 | + end | |
| 76 | + | |
| 73 | 77 | def target_notification_message |
| 74 | - description + "\n\n" + | |
| 75 | 78 | _("User \"%{user}\" just requested to create community %{community}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :user => self.requestor.name, :community => self.name } |
| 76 | 79 | end |
| 77 | 80 | |
| ... | ... | @@ -82,7 +85,7 @@ class CreateCommunity < Task |
| 82 | 85 | end |
| 83 | 86 | |
| 84 | 87 | def task_cancelled_message |
| 85 | - _("Your request for registering community %{community} at %{environment} was not approved by the environment administrator. The following explanation was given: \n\n%{explanation}") % { :community => self.name, :environment => self.environment, :explanation => self.closing_statement } | |
| 88 | + _("Your request for registering community %{community} at %{environment} was not approved by the environment administrator. The following explanation was given: \n\n%{explanation}") % { :community => self.name, :environment => self.environment, :explanation => self.reject_explanation } | |
| 86 | 89 | end |
| 87 | 90 | |
| 88 | 91 | def task_finished_message | ... | ... |
app/models/create_enterprise.rb
| ... | ... | @@ -11,23 +11,9 @@ class CreateEnterprise < Task |
| 11 | 11 | N_('Economic activity') |
| 12 | 12 | N_('Management information') |
| 13 | 13 | |
| 14 | - DATA_FIELDS = Enterprise.fields + %w[name identifier region_id reject_explanation] | |
| 15 | - | |
| 16 | - serialize :data, Hash | |
| 17 | - attr_protected :data | |
| 18 | - def data | |
| 19 | - self[:data] ||= Hash.new | |
| 20 | - end | |
| 21 | - | |
| 14 | + DATA_FIELDS = Enterprise.fields + %w[name identifier region_id] | |
| 22 | 15 | DATA_FIELDS.each do |field| |
| 23 | - # getter | |
| 24 | - define_method(field) do | |
| 25 | - self.data[field.to_sym] | |
| 26 | - end | |
| 27 | - # setter | |
| 28 | - define_method("#{field}=") do |value| | |
| 29 | - self.data[field.to_sym] = value | |
| 30 | - end | |
| 16 | + settings_items field.to_sym | |
| 31 | 17 | end |
| 32 | 18 | |
| 33 | 19 | # checks for virtual attributes |
| ... | ... | @@ -48,7 +34,6 @@ class CreateEnterprise < Task |
| 48 | 34 | |
| 49 | 35 | # check for explanation when rejecting |
| 50 | 36 | validates_presence_of :reject_explanation, :if => (lambda { |record| record.status == Task::Status::CANCELLED } ) |
| 51 | - | |
| 52 | 37 | xss_terminate :only => [ :acronym, :address, :contact_person, :contact_phone, :economic_activity, :legal_form, :management_information, :name ], :on => 'validation' |
| 53 | 38 | |
| 54 | 39 | def validate |
| ... | ... | @@ -59,7 +44,7 @@ class CreateEnterprise < Task |
| 59 | 44 | end |
| 60 | 45 | end |
| 61 | 46 | |
| 62 | - if self.identifier && Profile.exists?(:identifier => self.identifier) | |
| 47 | + if self.status != Task::Status::CANCELLED && self.identifier && Profile.exists?(:identifier => self.identifier) | |
| 63 | 48 | self.errors.add(:identifier, '%{fn} is already being as identifier by another enterprise, organization or person.') |
| 64 | 49 | end |
| 65 | 50 | end |
| ... | ... | @@ -91,7 +76,7 @@ class CreateEnterprise < Task |
| 91 | 76 | end |
| 92 | 77 | |
| 93 | 78 | def environment |
| 94 | - region ? region.environment : self.requestor ? self.requestor.environment : Environment.default | |
| 79 | + requestor.environment | |
| 95 | 80 | end |
| 96 | 81 | |
| 97 | 82 | def available_regions |
| ... | ... | @@ -153,8 +138,24 @@ class CreateEnterprise < Task |
| 153 | 138 | enterprise.add_admin(enterprise.user.person) |
| 154 | 139 | end |
| 155 | 140 | |
| 156 | - def description | |
| 157 | - _('Enterprise registration: "%s"') % self.name | |
| 141 | + def title | |
| 142 | + _("Enterprise registration") | |
| 143 | + end | |
| 144 | + | |
| 145 | + def icon | |
| 146 | + {:type => :defined_image, :src => '/images/icons-app/enterprise-minor.png', :name => name} | |
| 147 | + end | |
| 148 | + | |
| 149 | + def subject | |
| 150 | + name | |
| 151 | + end | |
| 152 | + | |
| 153 | + def information | |
| 154 | + {:message => _('%{requestor} wants to create enterprise %{subject}.')} | |
| 155 | + end | |
| 156 | + | |
| 157 | + def reject_details | |
| 158 | + true | |
| 158 | 159 | end |
| 159 | 160 | |
| 160 | 161 | def task_created_message |
| ... | ... | @@ -164,18 +165,18 @@ class CreateEnterprise < Task |
| 164 | 165 | end |
| 165 | 166 | |
| 166 | 167 | def task_finished_message |
| 167 | - _('Your request for registering the enterprise "%{enterprise}" was approved. You can access %{environment} now and provide start providing all relevant information your new enterprise.') % { :enterprise => self.name, :environment => self.environment } | |
| 168 | + __('Your request for registering the enterprise "%{enterprise}" was approved. You can access %{environment} now and provide start providing all relevant information your new enterprise.') % { :enterprise => self.name, :environment => self.environment } | |
| 168 | 169 | end |
| 169 | 170 | |
| 170 | 171 | def task_cancelled_message |
| 171 | - _("Your request for registering the enterprise %{enterprise} at %{environment} was NOT approved by the validator organization. The following explanation was given: \n\n%{explanation}") % { :enterprise => self.name, :environment => self.environment, :explanation => self.reject_explanation } | |
| 172 | + __("Your request for registering the enterprise %{enterprise} at %{environment} was NOT approved by the validator organization. The following explanation was given: \n\n%{explanation}") % { :enterprise => self.name, :environment => self.environment, :explanation => self.reject_explanation } | |
| 172 | 173 | end |
| 173 | 174 | |
| 174 | 175 | def target_notification_message |
| 175 | 176 | msg = "" |
| 176 | - msg << _("Enterprise \"%{enterprise}\" just requested to enter %{environment}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :enterprise => self.name, :environment => self.environment } | |
| 177 | + msg << __("Enterprise \"%{enterprise}\" just requested to enter %{environment}. You have to approve or reject it through the \"Pending Validations\" section in your control panel.\n") % { :enterprise => self.name, :environment => self.environment } | |
| 177 | 178 | msg << "\n" |
| 178 | - msg << _("The data provided by the enterprise was the following:\n") << "\n" | |
| 179 | + msg << __("The data provided by the enterprise was the following:\n") << "\n" | |
| 179 | 180 | |
| 180 | 181 | |
| 181 | 182 | msg << (_("Name: %s") % self.name) << "\n" |
| ... | ... | @@ -185,16 +186,20 @@ class CreateEnterprise < Task |
| 185 | 186 | msg << (_("Foundation Year: %d") % self.foundation_year) << "\n" unless self.foundation_year.blank? |
| 186 | 187 | msg << (_("Economic activity: %s") % self.economic_activity) << "\n" |
| 187 | 188 | |
| 188 | - msg << _("Information about enterprise's management:\n") << self.management_information.to_s << "\n" | |
| 189 | + msg << __("Information about enterprise's management:\n") << self.management_information.to_s << "\n" | |
| 189 | 190 | |
| 190 | 191 | msg << (_("Contact phone: %s") % self.contact_phone) << "\n" |
| 191 | 192 | msg << (_("Contact person: %s") % self.contact_person) << "\n" |
| 192 | 193 | |
| 193 | - msg << _('CreateEnterprise|Identifier') | |
| 194 | + msg << __('CreateEnterprise|Identifier') | |
| 194 | 195 | |
| 195 | 196 | msg |
| 196 | 197 | end |
| 197 | 198 | |
| 199 | + def target_notification_description | |
| 200 | + _('%{requestor} wants to create enterprise %{subject}.') % {:requestor => requestor.name, :subject => subject} | |
| 201 | + end | |
| 202 | + | |
| 198 | 203 | def permission |
| 199 | 204 | :validate_enterprise |
| 200 | 205 | end | ... | ... |
app/models/disabled_enterprise_message_block.rb
| 1 | 1 | class DisabledEnterpriseMessageBlock < Block |
| 2 | 2 | |
| 3 | 3 | def self.description |
| 4 | - _('"Disabled enterprise" message') | |
| 4 | + __('"Disabled enterprise" message') | |
| 5 | 5 | end |
| 6 | 6 | |
| 7 | 7 | def help |
| 8 | - _('Shows a message for disabled enterprises.') | |
| 8 | + __('Shows a message for disabled enterprises.') | |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | 11 | def default_title | ... | ... |
app/models/email_activation.rb
| ... | ... | @@ -11,8 +11,20 @@ class EmailActivation < Task |
| 11 | 11 | end |
| 12 | 12 | end |
| 13 | 13 | |
| 14 | - def description | |
| 15 | - _("'%{user} wants to activate email '%{email}'") % { :user => person.name, :email => person.email_addresses.join(', ') } | |
| 14 | + def title | |
| 15 | + _("Email activation") | |
| 16 | + end | |
| 17 | + | |
| 18 | + def subject | |
| 19 | + person.email_addresses.join(', ') | |
| 20 | + end | |
| 21 | + | |
| 22 | + def information | |
| 23 | + {:message => _("%{requestor} wants to activate the following email: %{subject}.")} | |
| 24 | + end | |
| 25 | + | |
| 26 | + def icon | |
| 27 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
| 16 | 28 | end |
| 17 | 29 | |
| 18 | 30 | def perform | ... | ... |
app/models/enterprise.rb
| ... | ... | @@ -4,7 +4,8 @@ class Enterprise < Organization |
| 4 | 4 | |
| 5 | 5 | N_('Enterprise') |
| 6 | 6 | |
| 7 | - has_many :products, :dependent => :destroy | |
| 7 | + has_many :products, :dependent => :destroy, :order => 'name ASC' | |
| 8 | + has_many :inputs, :through => :products | |
| 8 | 9 | |
| 9 | 10 | extra_data_for_index :product_categories |
| 10 | 11 | |
| ... | ... | @@ -51,6 +52,10 @@ class Enterprise < Organization |
| 51 | 52 | environment ? environment.active_enterprise_fields : [] |
| 52 | 53 | end |
| 53 | 54 | |
| 55 | + def highlighted_products_with_image(options = {}) | |
| 56 | + Product.find(:all, {:conditions => {:highlighted => true}, :joins => :image}.merge(options)) | |
| 57 | + end | |
| 58 | + | |
| 54 | 59 | def required_fields |
| 55 | 60 | environment ? environment.required_enterprise_fields : [] |
| 56 | 61 | end |
| ... | ... | @@ -117,17 +122,28 @@ class Enterprise < Organization |
| 117 | 122 | end |
| 118 | 123 | |
| 119 | 124 | def default_set_of_blocks |
| 125 | + links = [ | |
| 126 | + {:name => _("Enterprises's profile"), :address => '/profile/{profile}', :icon => 'ok'}, | |
| 127 | + {:name => _('Blog'), :address => '/{profile}/blog', :icon => 'edit'}, | |
| 128 | + {:name => _('Products'), :address => '/catalog/{profile}', :icon => 'new'}, | |
| 129 | + ] | |
| 120 | 130 | blocks = [ |
| 121 | - [MainBlock], | |
| 122 | - [ProfileInfoBlock, MembersBlock], | |
| 123 | - [RecentDocumentsBlock] | |
| 131 | + [MainBlock.new], | |
| 132 | + [ProfileImageBlock.new, LinkListBlock.new(:links => links)], | |
| 133 | + [] | |
| 124 | 134 | ] |
| 125 | 135 | if !environment.enabled?('disable_products_for_enterprises') |
| 126 | - blocks[2].unshift ProductsBlock | |
| 136 | + blocks[2].unshift ProductsBlock.new | |
| 127 | 137 | end |
| 128 | 138 | blocks |
| 129 | 139 | end |
| 130 | 140 | |
| 141 | + def default_set_of_articles | |
| 142 | + [ | |
| 143 | + Blog.new(:name => _('Blog')), | |
| 144 | + ] | |
| 145 | + end | |
| 146 | + | |
| 131 | 147 | before_create do |enterprise| |
| 132 | 148 | if enterprise.environment.enabled?('enterprises_are_disabled_when_created') |
| 133 | 149 | enterprise.enabled = false |
| ... | ... | @@ -149,10 +165,4 @@ class Enterprise < Organization |
| 149 | 165 | enable_contact_us |
| 150 | 166 | end |
| 151 | 167 | |
| 152 | - protected | |
| 153 | - | |
| 154 | - def default_homepage(attrs) | |
| 155 | - EnterpriseHomepage.new(attrs) | |
| 156 | - end | |
| 157 | - | |
| 158 | 168 | end | ... | ... |
app/models/enterprise_activation.rb
| ... | ... | @@ -2,7 +2,6 @@ class EnterpriseActivation < Task |
| 2 | 2 | |
| 3 | 3 | class RequestorRequired < Exception; end |
| 4 | 4 | |
| 5 | - acts_as_having_settings :field => :data | |
| 6 | 5 | settings_items :enterprise_id, :integer |
| 7 | 6 | |
| 8 | 7 | validates_presence_of :enterprise_id |
| ... | ... | @@ -20,4 +19,20 @@ class EnterpriseActivation < Task |
| 20 | 19 | self.enterprise.enable(requestor) |
| 21 | 20 | end |
| 22 | 21 | |
| 22 | + def title | |
| 23 | + _("Enterprise activation") | |
| 24 | + end | |
| 25 | + | |
| 26 | + def linked_subject | |
| 27 | + {:text => target.name, :url => target.public_profile_url} | |
| 28 | + end | |
| 29 | + | |
| 30 | + def information | |
| 31 | + {:message => _('%{requestor} wants to activate enterprise %{linked_subject}.')} | |
| 32 | + end | |
| 33 | + | |
| 34 | + def icon | |
| 35 | + {:type => :profile_image, :profile => requestor, :url => requestor.url} | |
| 36 | + end | |
| 37 | + | |
| 23 | 38 | end | ... | ... |
app/models/enterprise_homepage.rb
app/models/enterprises_block.rb
| ... | ... | @@ -28,29 +28,8 @@ class EnterprisesBlock < ProfileListBlock |
| 28 | 28 | end |
| 29 | 29 | end |
| 30 | 30 | |
| 31 | - def profile_count | |
| 32 | - if owner.kind_of?(Environment) | |
| 33 | - owner.enterprises.count(:conditions => { :visible => true }) | |
| 34 | - else | |
| 35 | - owner.enterprises(:visible => true).count | |
| 36 | - end | |
| 37 | - | |
| 38 | - end | |
| 39 | - | |
| 40 | - def profile_finder | |
| 41 | - @profile_finder ||= EnterprisesBlock::Finder.new(self) | |
| 42 | - end | |
| 43 | - | |
| 44 | - class Finder < ProfileListBlock::Finder | |
| 45 | - def ids | |
| 46 | - # FIXME when owner is an environment (i.e. listing enterprises globally | |
| 47 | - # this can become SLOW) | |
| 48 | - if block.owner.kind_of?(Environment) | |
| 49 | - block.owner.enterprises.all(:conditions => {:visible => true}, :limit => block.limit, :order => 'random()').map(&:id) | |
| 50 | - else | |
| 51 | - block.owner.enterprises.select(&:visible).map(&:id) | |
| 52 | - end | |
| 53 | - end | |
| 31 | + def profiles | |
| 32 | + owner.enterprises | |
| 54 | 33 | end |
| 55 | 34 | |
| 56 | 35 | end | ... | ... |
app/models/environment.rb
| ... | ... | @@ -3,6 +3,8 @@ |
| 3 | 3 | # domains. |
| 4 | 4 | class Environment < ActiveRecord::Base |
| 5 | 5 | |
| 6 | + has_many :users | |
| 7 | + | |
| 6 | 8 | self.partial_updates = false |
| 7 | 9 | |
| 8 | 10 | has_many :tasks, :dependent => :destroy, :as => 'target' |
| ... | ... | @@ -14,6 +16,7 @@ class Environment < ActiveRecord::Base |
| 14 | 16 | 'manage_environment_categories' => N_('Manage environment categories'), |
| 15 | 17 | 'manage_environment_roles' => N_('Manage environment roles'), |
| 16 | 18 | 'manage_environment_validators' => N_('Manage environment validators'), |
| 19 | + 'manage_environment_users' => N_('Manage environment users'), | |
| 17 | 20 | } |
| 18 | 21 | |
| 19 | 22 | module Roles |
| ... | ... | @@ -42,9 +45,7 @@ class Environment < ActiveRecord::Base |
| 42 | 45 | :name => N_('Member'), |
| 43 | 46 | :environment => self, |
| 44 | 47 | :permissions => [ |
| 45 | - 'edit_profile', | |
| 46 | - 'post_content', | |
| 47 | - 'manage_products' | |
| 48 | + 'invite_members', | |
| 48 | 49 | ] |
| 49 | 50 | ) |
| 50 | 51 | # moderators for enterprises, communities etc |
| ... | ... | @@ -67,7 +68,7 @@ class Environment < ActiveRecord::Base |
| 67 | 68 | end |
| 68 | 69 | |
| 69 | 70 | def admins |
| 70 | - self.members_by_role(Environment::Roles.admin(self.id)) | |
| 71 | + Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', Environment::Roles.admin(self).id]) | |
| 71 | 72 | end |
| 72 | 73 | |
| 73 | 74 | # returns the available features for a Environment, in the form of a |
| ... | ... | @@ -80,7 +81,7 @@ class Environment < ActiveRecord::Base |
| 80 | 81 | 'disable_asset_communities' => __('Disable search for communities'), |
| 81 | 82 | 'disable_asset_products' => _('Disable search for products'), |
| 82 | 83 | 'disable_asset_events' => _('Disable search for events'), |
| 83 | - 'disable_products_for_enterprises' => _('Disable products for enterprises'), | |
| 84 | + 'disable_products_for_enterprises' => __('Disable products for enterprises'), | |
| 84 | 85 | 'disable_categories' => _('Disable categories'), |
| 85 | 86 | 'disable_cms' => _('Disable CMS'), |
| 86 | 87 | 'disable_header_and_footer' => _('Disable header/footer editing by users'), |
| ... | ... | @@ -89,14 +90,12 @@ class Environment < ActiveRecord::Base |
| 89 | 90 | 'disable_select_city_for_contact' => _('Disable state/city select for contact form'), |
| 90 | 91 | 'disable_contact_person' => _('Disable contact for people'), |
| 91 | 92 | 'disable_contact_community' => _('Disable contact for groups/communities'), |
| 92 | - 'enterprise_registration' => _('Enterprise registration'), | |
| 93 | - 'join_community_popup' => _('Ask users to join a group/community with a popup'), | |
| 93 | + 'enterprise_registration' => __('Enterprise registration'), | |
| 94 | 94 | |
| 95 | - 'enterprise_activation' => _('Enable activation of enterprises'), | |
| 95 | + 'enterprise_activation' => __('Enable activation of enterprises'), | |
| 96 | 96 | 'wysiwyg_editor_for_environment_home' => _('Use WYSIWYG editor to edit environment home page'), |
| 97 | 97 | 'media_panel' => _('Media panel in WYSIWYG editor'), |
| 98 | 98 | 'select_preferred_domain' => _('Select preferred domains per profile'), |
| 99 | - 'display_wizard_signup' => _('Display wizard signup'), | |
| 100 | 99 | 'use_portal_community' => _('Use the portal as news source for front page'), |
| 101 | 100 | 'user_themes' => _('Allow users to create their own themes'), |
| 102 | 101 | 'search_in_home' => _("Display search form in home page"), |
| ... | ... | @@ -107,7 +106,10 @@ class Environment < ActiveRecord::Base |
| 107 | 106 | 'organizations_are_moderated_by_default' => _("Organizations have moderated publication by default"), |
| 108 | 107 | 'enable_organization_url_change' => _("Allow organizations to change their URL"), |
| 109 | 108 | 'admin_must_approve_new_communities' => _("Admin must approve creation of communities"), |
| 110 | - 'enterprises_are_disabled_when_created' => _('Enterprises are disabled when created'), | |
| 109 | + 'enterprises_are_disabled_when_created' => __('Enterprises are disabled when created'), | |
| 110 | + 'show_balloon_with_profile_links_when_clicked' => _('Show a balloon with profile links when a profile image is clicked'), | |
| 111 | + 'xmpp_chat' => _('XMPP/Jabber based chat'), | |
| 112 | + 'show_zoom_button_on_article_images' => _('Show a zoom link on all article images') | |
| 111 | 113 | } |
| 112 | 114 | end |
| 113 | 115 | |
| ... | ... | @@ -131,7 +133,8 @@ class Environment < ActiveRecord::Base |
| 131 | 133 | env.boxes[1].blocks << RecentDocumentsBlock.new |
| 132 | 134 | |
| 133 | 135 | # "right" area |
| 134 | - env.boxes[2].blocks << ProfileListBlock.new | |
| 136 | + env.boxes[2].blocks << CommunitiesBlock.new(:limit => 6) | |
| 137 | + env.boxes[2].blocks << PeopleBlock.new(:limit => 6) | |
| 135 | 138 | end |
| 136 | 139 | |
| 137 | 140 | # One Environment can be reached by many domains |
| ... | ... | @@ -147,10 +150,16 @@ class Environment < ActiveRecord::Base |
| 147 | 150 | has_many :categories |
| 148 | 151 | has_many :display_categories, :class_name => 'Category', :conditions => 'display_color is not null and parent_id is null', :order => 'display_color' |
| 149 | 152 | |
| 153 | + has_many :product_categories, :conditions => { :type => 'ProductCategory'} | |
| 150 | 154 | has_many :regions |
| 151 | 155 | |
| 152 | 156 | has_many :roles |
| 153 | 157 | |
| 158 | + has_many :qualifiers | |
| 159 | + has_many :certifiers | |
| 160 | + | |
| 161 | + has_many :mailings, :class_name => 'EnvironmentMailing', :foreign_key => :source_id, :as => 'source' | |
| 162 | + | |
| 154 | 163 | acts_as_accessible |
| 155 | 164 | |
| 156 | 165 | def superior_intances |
| ... | ... | @@ -198,16 +207,21 @@ class Environment < ActiveRecord::Base |
| 198 | 207 | settings_items :location, :type => String |
| 199 | 208 | settings_items :layout_template, :type => String, :default => 'default' |
| 200 | 209 | settings_items :homepage, :type => String |
| 201 | - settings_items :description, :type => String | |
| 202 | - settings_items :category_types, :type => Array, :default => ['Category'] | |
| 210 | + settings_items :description, :type => String, :default => '<div style="text-align: center"><a href="http://noosfero.org/"><img src="/images/noosfero-network.png" alt="Noosfero"/></a></div>' | |
| 203 | 211 | settings_items :enable_ssl |
| 204 | - settings_items :theme, :type => String, :default => 'default' | |
| 205 | - settings_items :icon_theme, :type => String, :default => 'default' | |
| 206 | 212 | settings_items :local_docs, :type => Array, :default => [] |
| 207 | 213 | settings_items :news_amount_by_folder, :type => Integer, :default => 4 |
| 208 | 214 | settings_items :help_message_to_add_enterprise, :type => String, :default => '' |
| 209 | 215 | settings_items :tip_message_enterprise_activation_question, :type => String, :default => '' |
| 210 | 216 | |
| 217 | + settings_items :currency_unit, :type => String, :default => '$' | |
| 218 | + settings_items :currency_separator, :type => String, :default => '.' | |
| 219 | + settings_items :currency_delimiter, :type => String, :default => ',' | |
| 220 | + | |
| 221 | + settings_items :trusted_sites_for_iframe, :type => Array, :default => ['itheora.org', 'tv.softwarelivre.org', 'stream.softwarelivre.org'] | |
| 222 | + | |
| 223 | + settings_items :enabled_plugins, :type => Array, :default => [] | |
| 224 | + | |
| 211 | 225 | def news_amount_by_folder=(amount) |
| 212 | 226 | settings[:news_amount_by_folder] = amount.to_i |
| 213 | 227 | end |
| ... | ... | @@ -241,6 +255,29 @@ class Environment < ActiveRecord::Base |
| 241 | 255 | end |
| 242 | 256 | end |
| 243 | 257 | |
| 258 | + def enabled_features | |
| 259 | + features = self.class.available_features | |
| 260 | + features.delete_if{ |k, v| !self.enabled?(k) } | |
| 261 | + end | |
| 262 | + | |
| 263 | + before_create :enable_default_features | |
| 264 | + def enable_default_features | |
| 265 | + %w( | |
| 266 | + disable_asset_products | |
| 267 | + disable_gender_icon | |
| 268 | + disable_products_for_enterprises | |
| 269 | + disable_select_city_for_contact | |
| 270 | + enterprise_registration | |
| 271 | + media_panel | |
| 272 | + organizations_are_moderated_by_default | |
| 273 | + show_balloon_with_profile_links_when_clicked | |
| 274 | + use_portal_community | |
| 275 | + wysiwyg_editor_for_environment_home | |
| 276 | + ).each do |feature| | |
| 277 | + enable(feature) | |
| 278 | + end | |
| 279 | + end | |
| 280 | + | |
| 244 | 281 | # returns <tt>true</tt> if this Environment has terms of use to be |
| 245 | 282 | # accepted by users before registration. |
| 246 | 283 | def has_terms_of_use? |
| ... | ... | @@ -457,6 +494,10 @@ class Environment < ActiveRecord::Base |
| 457 | 494 | |
| 458 | 495 | xss_terminate :only => [ :message_for_disabled_enterprise ], :with => 'white_list', :on => 'validation' |
| 459 | 496 | |
| 497 | + validates_presence_of :theme | |
| 498 | + | |
| 499 | + include WhiteListFilter | |
| 500 | + filter_iframes :message_for_disabled_enterprise, :whitelist => lambda { trusted_sites_for_iframe } | |
| 460 | 501 | |
| 461 | 502 | # ################################################# |
| 462 | 503 | # Business logic in general |
| ... | ... | @@ -525,7 +566,29 @@ class Environment < ActiveRecord::Base |
| 525 | 566 | end |
| 526 | 567 | |
| 527 | 568 | def themes=(values) |
| 528 | - settings[:themes] = values.map(&:id) | |
| 569 | + settings[:themes] = values | |
| 570 | + end | |
| 571 | + | |
| 572 | + def add_themes(values) | |
| 573 | + if settings[:themes].nil? | |
| 574 | + self.themes = values | |
| 575 | + else | |
| 576 | + settings[:themes] += values | |
| 577 | + end | |
| 578 | + end | |
| 579 | + | |
| 580 | + before_create do |env| | |
| 581 | + env.settings[:themes] ||= %w[ | |
| 582 | + aluminium | |
| 583 | + butter | |
| 584 | + chameleon | |
| 585 | + chocolate | |
| 586 | + noosfero | |
| 587 | + orange | |
| 588 | + plum | |
| 589 | + scarletred | |
| 590 | + skyblue | |
| 591 | + ] | |
| 529 | 592 | end |
| 530 | 593 | |
| 531 | 594 | def community_template |
| ... | ... | @@ -589,7 +652,7 @@ class Environment < ActiveRecord::Base |
| 589 | 652 | end |
| 590 | 653 | |
| 591 | 654 | def portal_folders |
| 592 | - (settings[:portal_folders] || []).map{|fid| portal_community.articles.find(fid) } | |
| 655 | + (settings[:portal_folders] || []).map{|fid| portal_community.articles.find(:first, :conditions => { :id => fid }) }.compact | |
| 593 | 656 | end |
| 594 | 657 | |
| 595 | 658 | def portal_folders=(folders) |
| ... | ... | @@ -628,4 +691,17 @@ class Environment < ActiveRecord::Base |
| 628 | 691 | end |
| 629 | 692 | end |
| 630 | 693 | |
| 694 | + def highlighted_products_with_image(options = {}) | |
| 695 | + Product.find(:all, {:conditions => {:highlighted => true, :enterprise_id => self.enterprises.find(:all, :select => :id) }, :joins => :image}.merge(options)) | |
| 696 | + end | |
| 697 | + | |
| 698 | + settings_items :home_cache_in_minutes, :type => :integer, :default => 5 | |
| 699 | + settings_items :general_cache_in_minutes, :type => :integer, :default => 15 | |
| 700 | + settings_items :profile_cache_in_minutes, :type => :integer, :default => 15 | |
| 701 | + | |
| 702 | + def image_galleries | |
| 703 | + portal_community ? portal_community.image_galleries : [] | |
| 704 | + end | |
| 705 | + | |
| 631 | 706 | end |
| 707 | + | ... | ... |
| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | +class EnvironmentMailing < Mailing | |
| 2 | + | |
| 3 | + def recipients(offset=0, limit=100) | |
| 4 | + source.people.all(:order => :id, :offset => offset, :limit => limit, :joins => "LEFT OUTER JOIN mailing_sents m ON (m.mailing_id = #{id} AND m.person_id = profiles.id)", :conditions => { "m.person_id" => nil }) | |
| 5 | + end | |
| 6 | + | |
| 7 | + def each_recipient | |
| 8 | + offset = 0 | |
| 9 | + limit = 100 | |
| 10 | + while !(people = recipients(offset, limit)).empty? | |
| 11 | + people.each do |person| | |
| 12 | + yield person | |
| 13 | + end | |
| 14 | + offset = offset + limit | |
| 15 | + end | |
| 16 | + end | |
| 17 | + | |
| 18 | + def signature_message | |
| 19 | + _('Sent by %s.') % source.name | |
| 20 | + end | |
| 21 | + | |
| 22 | + def url | |
| 23 | + source.top_url | |
| 24 | + end | |
| 25 | +end | ... | ... |
app/models/environment_statistics_block.rb
| ... | ... | @@ -17,11 +17,12 @@ class EnvironmentStatisticsBlock < Block |
| 17 | 17 | enterprises = owner.enterprises.visible.count |
| 18 | 18 | communities = owner.communities.visible.count |
| 19 | 19 | |
| 20 | - info = [ | |
| 21 | - n_('One user', '%{num} users', users) % { :num => users }, | |
| 22 | - n__('One enterprise', '%{num} enterprises', enterprises) % { :num => enterprises }, | |
| 23 | - n__('One community', '%{num} communities', communities) % { :num => communities }, | |
| 24 | - ] | |
| 20 | + info = [] | |
| 21 | + info << (n_('One user', '%{num} users', users) % { :num => users }) | |
| 22 | + unless owner.enabled?('disable_asset_enterprises') | |
| 23 | + info << (n__('One enterprise', '%{num} enterprises', enterprises) % { :num => enterprises }) | |
| 24 | + end | |
| 25 | + info << (n__('One community', '%{num} communities', communities) % { :num => communities }) | |
| 25 | 26 | |
| 26 | 27 | block_title(title) + content_tag('ul', info.map {|item| content_tag('li', item) }.join("\n")) |
| 27 | 28 | end | ... | ... |
app/models/event.rb
| 1 | 1 | class Event < Article |
| 2 | 2 | |
| 3 | - acts_as_having_settings :field => :body | |
| 4 | - | |
| 5 | - settings_items :description, :type => :string | |
| 6 | - settings_items :link, :type => :string | |
| 7 | 3 | settings_items :address, :type => :string |
| 8 | 4 | |
| 5 | + def link=(value) | |
| 6 | + self.setting[:link] = maybe_add_http(value) | |
| 7 | + end | |
| 8 | + | |
| 9 | + def link | |
| 10 | + maybe_add_http(self.setting[:link]) | |
| 11 | + end | |
| 12 | + | |
| 9 | 13 | xss_terminate :only => [ :link ], :on => 'validation' |
| 10 | - xss_terminate :only => [ :description, :link, :address ], :with => 'white_list', :on => 'validation' | |
| 14 | + xss_terminate :only => [ :body, :link, :address ], :with => 'white_list', :on => 'validation' | |
| 15 | + | |
| 16 | + def initialize(*args) | |
| 17 | + super(*args) | |
| 18 | + self.start_date ||= Date.today | |
| 19 | + end | |
| 11 | 20 | |
| 12 | 21 | validates_presence_of :title, :start_date |
| 13 | 22 | |
| ... | ... | @@ -21,6 +30,9 @@ class Event < Article |
| 21 | 30 | {:conditions => ['start_date = :date AND end_date IS NULL OR (start_date <= :date AND end_date >= :date)', {:date => date}]} |
| 22 | 31 | } |
| 23 | 32 | |
| 33 | + include WhiteListFilter | |
| 34 | + filter_iframes :body, :link, :address, :whitelist => lambda { profile && profile.environment && profile.environment.trusted_sites_for_iframe } | |
| 35 | + | |
| 24 | 36 | def self.description |
| 25 | 37 | _('A calendar event') |
| 26 | 38 | end |
| ... | ... | @@ -29,7 +41,7 @@ class Event < Article |
| 29 | 41 | _('Event') |
| 30 | 42 | end |
| 31 | 43 | |
| 32 | - def icon_name | |
| 44 | + def self.icon_name(article = nil) | |
| 33 | 45 | 'event' |
| 34 | 46 | end |
| 35 | 47 | |
| ... | ... | @@ -88,26 +100,27 @@ class Event < Article |
| 88 | 100 | } |
| 89 | 101 | } |
| 90 | 102 | |
| 91 | - if self.description | |
| 103 | + if self.body | |
| 92 | 104 | html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description') |
| 93 | 105 | end |
| 94 | 106 | } |
| 95 | 107 | |
| 96 | - if self.description | |
| 97 | - result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.description) | |
| 108 | + if self.body | |
| 109 | + result.sub!('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', self.body) | |
| 98 | 110 | end |
| 99 | 111 | |
| 100 | 112 | result |
| 101 | 113 | end |
| 102 | 114 | |
| 103 | - def link=(value) | |
| 104 | - self.body[:link] = maybe_add_http(value) | |
| 115 | + def event? | |
| 116 | + true | |
| 105 | 117 | end |
| 106 | 118 | |
| 107 | - def link | |
| 108 | - maybe_add_http(self.body[:link]) | |
| 119 | + def tiny_mce? | |
| 120 | + true | |
| 109 | 121 | end |
| 110 | 122 | |
| 123 | + include Noosfero::TranslatableContent | |
| 111 | 124 | include MaybeAddHttp |
| 112 | 125 | |
| 113 | 126 | end | ... | ... |
app/models/favorite_enterprises_block.rb
| ... | ... | @@ -9,7 +9,7 @@ class FavoriteEnterprisesBlock < ProfileListBlock |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | 11 | def self.description |
| 12 | - __('Favorite enterprises') | |
| 12 | + __('Favorite Enterprises') | |
| 13 | 13 | end |
| 14 | 14 | |
| 15 | 15 | def footer |
| ... | ... | @@ -20,18 +20,8 @@ class FavoriteEnterprisesBlock < ProfileListBlock |
| 20 | 20 | end |
| 21 | 21 | end |
| 22 | 22 | |
| 23 | - def profile_count | |
| 24 | - owner.favorite_enterprises.count | |
| 25 | - end | |
| 26 | - | |
| 27 | - def profile_finder | |
| 28 | - @profile_finder ||= FavoriteEnterprisesBlock::Finder.new(self) | |
| 29 | - end | |
| 30 | - | |
| 31 | - class Finder < ProfileListBlock::Finder | |
| 32 | - def ids | |
| 33 | - block.owner.favorite_enterprises.map(&:id) | |
| 34 | - end | |
| 23 | + def profiles | |
| 24 | + owner.favorite_enterprises | |
| 35 | 25 | end |
| 36 | 26 | |
| 37 | 27 | end | ... | ... |
| ... | ... | @@ -0,0 +1,35 @@ |
| 1 | +class FeaturedProductsBlock < Block | |
| 2 | + | |
| 3 | + settings_items :product_ids, :type => Array, :default => [] | |
| 4 | + settings_items :groups_of, :type => :integer, :default => 3 | |
| 5 | + settings_items :speed, :type => :integer, :default => 1000 | |
| 6 | + settings_items :reflect, :type => :boolean, :default => true | |
| 7 | + | |
| 8 | + before_save do |block| | |
| 9 | + if block.owner.kind_of?(Environment) && block.product_ids.blank? | |
| 10 | + seed = block.owner.products.count | |
| 11 | + block.product_ids = block.owner.highlighted_products_with_image(:offset => (rand(seed) % (seed - block.groups_of * 3)), :limit => block.groups_of * 3).map(&:id) | |
| 12 | + end | |
| 13 | + block.groups_of = block.groups_of.to_i | |
| 14 | + end | |
| 15 | + | |
| 16 | + def self.description | |
| 17 | + _('Featured Products') | |
| 18 | + end | |
| 19 | + | |
| 20 | + def products | |
| 21 | + Product.find(self.product_ids) || [] | |
| 22 | + end | |
| 23 | + | |
| 24 | + def products_for_selection | |
| 25 | + self.owner.highlighted_products_with_image | |
| 26 | + end | |
| 27 | + | |
| 28 | + def content | |
| 29 | + block = self | |
| 30 | + lambda do | |
| 31 | + render :file => 'blocks/featured_products', :locals => { :block => block } | |
| 32 | + end | |
| 33 | + end | |
| 34 | + | |
| 35 | +end | ... | ... |
app/models/feed_reader_block.rb
| ... | ... | @@ -12,7 +12,7 @@ class FeedReaderBlock < Block |
| 12 | 12 | def address=(new_address) |
| 13 | 13 | old_address = address |
| 14 | 14 | orig_set_address(new_address) |
| 15 | - self.enabled = (old_address.blank? && !new_address.blank?) || (new_address && new_address != old_address) || false | |
| 15 | + self.enabled = (new_address && new_address != old_address) || (new_address && self.enabled) || false | |
| 16 | 16 | end |
| 17 | 17 | |
| 18 | 18 | settings_items :limit, :type => :integer | ... | ... |
app/models/folder.rb
| ... | ... | @@ -2,19 +2,10 @@ class Folder < Article |
| 2 | 2 | |
| 3 | 3 | acts_as_having_settings :field => :setting |
| 4 | 4 | |
| 5 | - settings_items :view_as, :type => :string, :default => 'folder' | |
| 6 | - | |
| 7 | 5 | xss_terminate :only => [ :body ], :with => 'white_list', :on => 'validation' |
| 8 | 6 | |
| 9 | - def self.select_views | |
| 10 | - [[_('Folder'), 'folder'], [_('Image gallery'), 'image_gallery']] | |
| 11 | - end | |
| 12 | - | |
| 13 | - def self.views | |
| 14 | - select_views.map(&:last) | |
| 15 | - end | |
| 16 | - | |
| 17 | - validates_inclusion_of :view_as, :in => self.views | |
| 7 | + include WhiteListFilter | |
| 8 | + filter_iframes :body, :whitelist => lambda { profile && profile.environment && profile.environment.trusted_sites_for_iframe } | |
| 18 | 9 | |
| 19 | 10 | def self.short_description |
| 20 | 11 | _('Folder') |
| ... | ... | @@ -24,37 +15,22 @@ class Folder < Article |
| 24 | 15 | _('A folder, inside which you can put other articles.') |
| 25 | 16 | end |
| 26 | 17 | |
| 27 | - def icon_name | |
| 18 | + def self.icon_name(article = nil) | |
| 28 | 19 | 'folder' |
| 29 | 20 | end |
| 30 | 21 | |
| 31 | - | |
| 22 | + include ActionView::Helpers::TagHelper | |
| 32 | 23 | def to_html(options = {}) |
| 33 | - send(view_as) | |
| 34 | - end | |
| 35 | - | |
| 36 | - def folder | |
| 37 | 24 | folder = self |
| 38 | 25 | lambda do |
| 39 | 26 | render :file => 'content_viewer/folder', :locals => { :folder => folder } |
| 40 | 27 | end |
| 41 | 28 | end |
| 42 | 29 | |
| 43 | - def image_gallery | |
| 44 | - article = self | |
| 45 | - lambda do | |
| 46 | - render :file => 'content_viewer/image_gallery', :locals => {:article => article} | |
| 47 | - end | |
| 48 | - end | |
| 49 | - | |
| 50 | 30 | def folder? |
| 51 | 31 | true |
| 52 | 32 | end |
| 53 | 33 | |
| 54 | - def display_as_gallery? | |
| 55 | - view_as == 'image_gallery' | |
| 56 | - end | |
| 57 | - | |
| 58 | 34 | def can_display_hits? |
| 59 | 35 | false |
| 60 | 36 | end |
| ... | ... | @@ -70,7 +46,5 @@ class Folder < Article |
| 70 | 46 | has_many :images, :class_name => 'Article', |
| 71 | 47 | :foreign_key => 'parent_id', |
| 72 | 48 | :order => 'articles.type, articles.name', |
| 73 | - :include => :reference_article, | |
| 74 | - :conditions => ["articles.type = 'UploadedFile' and articles.content_type in (?) or articles.type = 'Folder' or (articles.type = 'PublishedArticle' and reference_articles_articles.type = 'UploadedFile' and reference_articles_articles.content_type in (?))", UploadedFile.content_types, UploadedFile.content_types] | |
| 75 | - | |
| 49 | + :conditions => ["articles.type = 'UploadedFile' and articles.content_type in (?) or articles.type in ('Folder','Gallery')", UploadedFile.content_types] | |
| 76 | 50 | end | ... | ... |
| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | +class Forum < Folder | |
| 2 | + | |
| 3 | + acts_as_having_posts :order => 'updated_at DESC' | |
| 4 | + | |
| 5 | + def self.short_description | |
| 6 | + _('Forum') | |
| 7 | + end | |
| 8 | + | |
| 9 | + def self.description | |
| 10 | + _('An internet forum, also called message board, where discussions can be held.') | |
| 11 | + end | |
| 12 | + | |
| 13 | + include ActionView::Helpers::TagHelper | |
| 14 | + def to_html(options = {}) | |
| 15 | + lambda do | |
| 16 | + render :file => 'content_viewer/forum_page' | |
| 17 | + end | |
| 18 | + end | |
| 19 | + | |
| 20 | + def forum? | |
| 21 | + true | |
| 22 | + end | |
| 23 | + | |
| 24 | + def self.icon_name(article = nil) | |
| 25 | + 'forum' | |
| 26 | + end | |
| 27 | +end | ... | ... |
app/models/friends_block.rb
| ... | ... | @@ -19,18 +19,8 @@ class FriendsBlock < ProfileListBlock |
| 19 | 19 | end |
| 20 | 20 | end |
| 21 | 21 | |
| 22 | - class FriendsBlock::Finder < ProfileListBlock::Finder | |
| 23 | - def ids | |
| 24 | - self.block.owner.friend_ids | |
| 25 | - end | |
| 26 | - end | |
| 27 | - | |
| 28 | - def profile_finder | |
| 29 | - @profile_finder ||= FriendsBlock::Finder.new(self) | |
| 30 | - end | |
| 31 | - | |
| 32 | - def profile_count | |
| 33 | - owner.friends.visible.count | |
| 22 | + def profiles | |
| 23 | + owner.friends | |
| 34 | 24 | end |
| 35 | 25 | |
| 36 | 26 | end | ... | ... |