Author: Braulio Bhavamitra <braulio@prout.io>
Merge branch 'master' of https://gitlab.com/noosfero/noosfero into stores
%!v(PANIC=String method: strings: negative Repeat count)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 47a684f6ef98dc5f4c09db8fd7875389ca56eee0..ad3b65b704766fda54e586451296385f39335d57 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,6 +15,10 @@ #smoke: # script: bundle exec rake ci:smoke # stage: smoke-tests +specs-models: + script: bundle exec rake spec SPEC=spec/models + stage: all-tests + api: script: bundle exec rake test:api stage: all-tests diff --git a/.travis.yml b/.travis.yml index 78739698cd9eb236690c650eadc4e39595a3adfc..156959f2cbbce10bd10c57d950709606c1b8a9a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,25 +12,32 @@ - "chat.freenode.net#noosfero-br" template: - "%{repository_slug} %{branch} %{commit} %{commit_subject} - %{result} %{build_url}" -# Ensure Container-based environment, as others can have some random failures -# specially with different Firefox versions and selenium tests. -# E.g. https://travis-ci.org/noosfero/noosfero/jobs/122918772#L1308 -# -# Also container-based environments have the fatest boot times and -# are the only one with cache available for public projects. +# Ensure Container-based environment for faster boot times. # See https://docs.travis-ci.com/user/ci-environment/#Virtualization-environments sudo: false -cache: bundler + +## +# Stick with precise as trusty is at least 50% slower. +# +#dist: trusty + +jdk: + # openjdk8 isn't available on precise + - oraclejdk8 language: ruby rvm: - - 2.3.3 + - 2.4.1 + +cache: bundler services: - postgresql - - elasticsearch addons: + postgresql: '9.4' + # same as in docker ci image + #firefox: '45.7.0' apt: packages: - tango-icon-theme @@ -38,8 +45,7 @@ - pidgin-data artifacts: paths: - $(ls tmp/artifact* | tr "\n" ":") - # same as in docker ci image - firefox: '45.7.0' + before_install: # geckodriver, from http://www.columbia.edu/~njn2118/journal/2016/10/28.html @@ -47,6 +53,10 @@ - wget https://github.com/mozilla/geckodriver/releases/download/v0.14.0/geckodriver-v0.14.0-linux64.tar.gz - mkdir geckodriver - tar -xzf geckodriver-v0.14.0-linux64.tar.gz -C geckodriver - export PATH=$PATH:$PWD/geckodriver + # elasticsearch 2 + - wget ${ES_DOWNLOAD_URL} + - tar -xzf elasticsearch-${ES_VERSION}.tar.gz + - ./elasticsearch-${ES_VERSION}/bin/elasticsearch & before_script: - mkdir -p tmp/{pids,cache} log cache @@ -60,8 +70,11 @@ - bundle exec rake db:migrate &>/dev/null env: global: + - ES_VERSION=1.7.6 + - ES_DOWNLOAD_URL=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz - LANG=en matrix: + - TASK=spec SPEC=spec/models - TASK=test:api - TASK=test:units - TASK=test:functionals diff --git a/AUTHORS.md b/AUTHORS.md index 85bb5c8eeb7d7d15d56cb33d8dfee8c37439253a..3ac5d0be80a9c1f5b34291bc51eac3ac5d64488a 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -29,12 +29,14 @@ Athos RibeiroAurelio A. Heckert <aurium@colivre.coop.br> Becca Cook <b.cook28@gmail.com> Braulio Bhavamitra <braulio@eita.org.br> +Braulio Bhavamitra <braulio@prout.io> Brenddon Gontijo <brenddongontijo@msn.com> Caio Formiga <caio.formiga@gmail.com> Caio SBA <caio@colivre.coop.br> Caio Salgado <caio.csalgado@gmail.com> Caio Tiago Oliveira <caiotiago@colivre.coop.br> Carlos Andre de Souza <carlos.andre.souza@msn.com> +Carlos Duque Guasch <c.duqueguasch@gmx.com> Carlos Morais <carlos88morais@gmail.com> Carlos Purificacao <carloseugenio@gmail.com> Christophe DANIEL <papaeng@gmail.com> @@ -69,17 +71,22 @@ Gustavo Cavalcante Gustavo Coelho <gust.rod.coelho@gmail.com> Gustavo Jaruga <darkshades@gmail.com> Gustavo Jaruga <darksshades@gmail.com> +Hannes Olszewski <sorro17@gmail.com> Hebert Douglas <hebertdougl@gmail.com> Hugo Melo <hugo@riseup.net> +Iago Rodrigues <iago006@hotmail.com> Iago Rodrigues <iago006@hotmailcom> Iolane Andrade <andrade.icaa@gmail.com> Iryna Pruitt <jdpruitt2807@prodigy.net> Isaac Canan <isaac@intelletto.com.br> Italo Valcy <italo@dcc.ufba.br> +Italo Valcy <italovalcy@ufba.br> Izharul Haq <atoz.chevara.2013@gmail.com> Jefferson Fernandes <jeffs.fernandes@gmail.com> Joenio Costa <joenio@colivre.coop.br> Joenio Costa <joenio@joenio.me> +Josafá Souza Jr <josafa.souza@serpro.gov.br> +Josafá Souza Jr <josafassj@gmail.com> Jose Pedro <1jpsneto@gmail.com> Josef Spillner <josef.spillner@tu-dresden.de> João M. M. da Silva <jaodsilv@linux.ime.usp.br> @@ -137,6 +144,7 @@ Rafael Gomes Rafael Martins <rmmartins@gmail.com> Rafael Reggiani Manzo <rr.manzo@gmail.com> Rafael de Souza Queiroz <querafael@live.com> +Raphael Matos da Costa <mdc.rapha@gmail.com> Raphaël Rousseau <raph@r4f.org> Raquel <rcordioli@gmail.com> Raquel Lira <raquel.lira@gmail.com> @@ -156,16 +164,20 @@ Thiago Casotti Thiago Kairala <thiagor.kairala@gmail.com> Thiago Ribeiro <thiagitosouza@hotmail.com> Thiago Zoroastro <thiago.zoroastro@bol.com.br> +Thomas Zach <tz2@aon.at> Tuux <tuxa@galaxie.eu.org> Valessio Brito <contato@valessiobrito.com.br> Valet 322 <petymakar@gmail.com> Victor Costa <vfcosta@gmail.com> Victor Hugo Alves de Carvalho <victorhugodf.ac@gmail.com> Victor Navarro <victor.matias.navarro@gmail.com> +Vinicius Borges <vbda123@gmail.com> Vinicius Brand <viniciuscb@gmail.com> Vitor Barbosa <vitornga15@gmail.com> Wilton Rodrigues <braynwilton@gmail.com> Yann Lugrin <yann.lugrin@liquid-concept.ch> +matheuslacerda <matheusmirandalacerda@gmail.com> +mendesiasmin <mendesiasmin96@gmail.com> Ábner Silva de Oliveira <abner.oliveira@serpro.gov.br> Álvaro Fernando <alvarofernandoms@gmail.com> Вадим Кардашьян <iosphone77@gmail.com> diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 931288b9f0f06c742f667c54dffb537c9d4a6f02..0000000000000000000000000000000000000000 --- a/CHANGELOG +++ /dev/null @@ -1,12 +0,0 @@ -If you made any significant change to the code that you consider worth being -reminded on the Release Notes, also include a correspondent entry here. If you -are not sure in which release your code will be released, include it on the -latest release and leave it to the commiter or RM responsible for it. - -v 1.5.0 (unreleased) - - Allow groups to disable admin email notificationo - - Add option to HighlightsBlock to open link in a new tab - - Move blocks html generation from models to helpers - -v 1.4.0 - - Migration from Rails 3 to Rails 4! diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..3a2526c45b7059d650331d27f173f8b0e6b33c82 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,284 @@ +# 1.8.0 + * Bumping version 1.8.0 (Rodrigo Souto) [fc45320] + * Updating authors file (Rodrigo Souto) [3922c14] + * Update translations (Rodrigo Souto) [48b331e] + * release-task: add default release message (Rodrigo Souto) [6ba0bee] + * changelog: update changelog automatically on release (Rodrigo Souto) [e5711c7] + * Adds public pages to PublicAccessRestriction plugin (Gabriel Silva) [f76ee68] + * join-not-logged: return to the profile page after login (Rodrigo Souto) [dcf7599] + * hstore: remove obsolete code (Rodrigo Souto) [2ad4801] + * Correção no comportamento de entrar na comunidade quando usuário não está logado (Raphael Matos da Costa) [56e5c90] + * Add subdomain and request id tags to log lines. It helps to follow the request event sequence in a multi-process service. (Aurélio A. Heckert) [d91239c] + * travis: use elasticsearch 1.7.6 (Braulio Bhavamitra) [9417b46] + * travis: use postgresql 9.4 (Braulio Bhavamitra) [8bd9688] + * Don't try to add hstore fields (Braulio Bhavamitra) [fca61b2] + * api: improve error handling for models (Victor Costa) [0bdcae8] + * Move from hstore to jsonb (no extension needed!) (Braulio Bhavamitra) [6c5b8ee] + * Available blocks including plugin blocks (Evandro Jr) [d269bab] + * Fix Push Notification Plugin (Tallys Martins) [a8aca1c] + * adding global css to session theme (Leandro Nunes dos Santos) [cdd072c] + * check if target exist (Marcelo Júnior) [b646328] + * Fix to always show members of a community in the menu block (Josafá Souza Jr) [03dc9f0] + * travis: update to working elasticsearch version (Braulio Bhavamitra) [71d253b] + * circleci: ensure vendor/bundle caching (Braulio Bhavamitra) [472aa1e] + * circleci: add elasticsearch and other updates (Braulio Bhavamitra) [4130072] + * Support ruby 2.4 (Braulio Bhavamitra) [ecb79b8] + * CI: run models specs (Braulio Bhavamitra) [f5e51c1] + * plugins/routes: use reverse_merge (fix products and other plugins' tests) (Braulio Bhavamitra) [4ffd926] + * plugins/routes: fix syntax for ruby 2.1 (Braulio Bhavamitra) [d02abb5] + * gitignore.example: add config/Gemfile (Braulio Bhavamitra) [f3da786] + * plugins: refactor routes to don't generate for uniexistant controllers (Braulio Bhavamitra) [69d08ab] + * Add spring restart configuration (Braulio Bhavamitra) [282cb51] + * gitignore.example: organize and add .swo vim files (Braulio Bhavamitra) [156e3a8] + * db: schema/migration: add missing hstore extension (Braulio Bhavamitra) [dafe82f] + * debian-control: move postgresql from recomends to depends (Rodrigo Souto) [d6b5242] + * Core support for Hstore (Rodrigo Souto) [6c44ead] + * test-factory: add working defaults for Article fast_create (Rodrigo Souto) [ee47366] + * Fixes event handling of popover menu triggers (Gabriel Silva) [73457c3] + * rspec: basic setup for use (Rodrigo Souto) [f964a75] + * release-task: remove obsolete reference for sqlite config (Rodrigo Souto) [4bd43bd] + * debian-install: install dotenv files (Rodrigo Souto) [a308b12] + * organizations-view: fix broken symbolic link (Rodrigo Souto) [e7b5789] + * Social Statistics Plugin (Rodrigo Souto) [70e0756] + * creating a settings endpoint (Leandro Nunes dos Santos) [1cfdeb9] + * avoid the possibility to save all blocks and removing available blocks from blocks file (Leandro Nunes dos Santos) [3b95854] + * api: update block position (Victor Costa) [c4b25d4] + * api: list available blocks for profile and environment (Victor Costa) [db889e1] + * migration: fix migration timestamps (Rodrigo Souto) [6f58b86] + * fix unit tests (Leandro Nunes dos Santos) [efab24d] + * Fix errors 400 to 422 when the call is correct but the data is rejected by the service (Josafá Souza Jr) [c412c51] + * Adds location field, refactors location selection (Gabriel Silva) [7f92d45] + * Fix lib requires (Braulio Bhavamitra) [1edeaef] + * Use require_relative everywhere (Braulio Bhavamitra) [0fcffea] + * Revert "travis tmp" (Braulio Bhavamitra) [07ce1da] + * Revert "elasticsearch: try fix" (Braulio Bhavamitra) [9fb3fb6] + * elasticsearch: try fix (Braulio Bhavamitra) [3a1cef8] + * travis tmp (Braulio Bhavamitra) [357cd93] + * change the invitation return to Entities::Response (Leandro Nunes dos Santos) [e35850f] + * adding unit tests for invite endpoint (Leandro Nunes dos Santos) [cc32c27] + * adding invite endpoint (Leandro Nunes dos Santos) [ae842f5] + * api: accept type when creating a new block (Victor Costa) [1ae02b8] + * Api render model errors (Evandro Jr) [bc9bda7] + * refactoring tags stuff to make only one behavior (Leandro Nunes dos Santos) [c4a4fe5] + * update api tags test (Marcelo Júnior) [9091901] + * update endpoints (Marcelo Júnior) [e69e6c9] + * Fixes community search when spreading w/ SubOrgPlugin (Gabriel Silva) [c571a63] + * Fixing range in events list (Victor Navarro) [d7fbb4a] + * Add default method that set api content for blocks (Victor Costa) [8d44ed1] + * recent_activities_plugin: fix date translation (Victor Costa) [b73833f] + * rack_timeout: optionally use it for better timeout handling (Hugo Melo) [52e7089] + * Fix work assignments plugin pagination cache (pedrodelyra) [a2ee0cd] + * rollbar: ignore bots' errors (Braulio Bhavamitra) [ba4490f] + * pg-search: facets implementation (Rodrigo Souto) [6b49a56] + * menu_block: fix permissions for profile links (Victor Costa) [5da2cb0] + * menu_block: add dynamic links to menu (Josafá Souza Jr) [867dfb2] + * person_test: try fix for random failures (Braulio Bhavamitra) [944e253] + * products: fix and move featured products view from core (Braulio Bhavamitra) [b219ae3] + * Adapt any image to use all highlights area (Aurélio A. Heckert) [18214fc] + * sniffer: fix typos in interests block (Hugo Melo) [19d1e4e] + * api: add closed attribute to community entity (Josafá Souza Jr) [346e774] + * token-input: use empty string as placeholder default (Rodrigo Souto) [bcd4057] + * script: add script/server to start only unicorn (Rodrigo Souto) [ddde5f1] + * api: change blocks when edit an environment (Victor Costa) [380dd13] + * api: change blocks when edit a profile (Victor Costa) [0141317] + * [New Plugin] Custom Routes (Gabriel Silva) [8292518] + * Fix redirection after uploading files (Pedro de Lyra) [134e4e6] + * Adds html_safe to work_assignment plugin articles (Tallys Martins) [ff8a72d] + * ldap_plugin: fix authentication for api (Victor Costa) [ab85ed6] + * api: use plugins when authenticate (Victor Costa) [26e5bbd] + * routes: allow plugins to use custom routes (Rodrigo Souto) [7cd87f2] + * fix unit test (Leandro Nunes dos Santos) [a42edbe] + * fix unit tests (Leandro Nunes dos Santos) [7fa1560] + * fix profile editor functional tests (Leandro Nunes dos Santos) [18b5e2d] + * fix recent block unit tests (Leandro Nunes dos Santos) [34a4148] + * refactoring profile additional data (Leandro Nunes dos Santos) [a430926] + * Added API method to change user password (Josafá Souza Jr) [798c5ce] + * Adds html_safe on new article mail notification (Gabriel Silva) [f383f85] + * Ignores erorrs when rendering the content summary (Gabriel Silva) [8cb7a78] + * Fixes reply_scrap_on_self notification info (Gabriel Silva) [1664c4c] + * api: fix endpoint that destroy profiles (Victor Costa) [03cf8ba] + * refactoring unit tests and creating some tests helpers (Leandro Nunes dos Santos) [5b73867] + * adjunting api to return fields that does not has values yet (Leonardo Soares) [a35d039] + * local tests (Leonardo Soares) [7a8101b] + * Makes sidebox's description html safe (Victor Navarro) [7859ccf] + * api: accept search params in friends endpoint (Victor Costa) [cbd282e] + * api: deprecate search endpoint (Victor Costa) [9e959ff] + * api: use search engine when filtering a collection (Victor Costa) [d169d3b] + * friendly-mime: human names for mime_type (Rodrigo Souto) [1cde2a2] + * compact-profile: use abbreviated date (Rodrigo Souto) [05deef7] + * layout-helper: remove unnecessary spaces (Rodrigo Souto) [17b4341] + * api: assign profile roles to person (Victor Costa) [78e9e67] + * api: expose assigned roles of a person in a profile (Victor Costa) [a74e197] + * layout-helper: fix broken tests (Victor Costa) [d223a04] + * chat: history min-width to avoid blank history (Rodrigo Souto) [86b9c07] + * profile-image: allow theme to define image size (Rodrigo Souto) [0c8d271] + * html-safe: article source (Rodrigo Souto) [d41ca60] + * person-form: display schooling (Rodrigo Souto) [3a9c6cd] + * layout-helper: fix body css classes (Rodrigo Souto) [2260332] + * api: return network activities from a profile (Josafá Souza Jr) [65a8c00] + * api: add/remove friend endpoint (Josafá Souza Jr) [c827f4f] + * Shows the author in article versions list (Gabriel Silva) [c89eb2a] + * Fix article translations button (pedrodelyra) [5f9e235] + * Cleans session when after captcha is answered (Gabriel Silva) [c3ecf05] + * puma: reconnect cache (Braulio Bhavamitra) [35b2fca] + * puma: clear connections to avoid leaks/crashes (Braulio Bhavamitra) [34948ba] + * text_article: add missing redcloth require (Braulio Bhavamitra) [3b77f83] + * rails5: remove unecessary controller requires (Braulio Bhavamitra) [fc899f5] + * products: hotspots: add and fix default values (Braulio Bhavamitra) [ac31d1b] + * products: add missing autocomplete infrastructure (Braulio Bhavamitra) [2321960] + * Basic support for jruby (Braulio Bhavamitra) [3601bf5] + * Configure mailgun and rollbar if tokens are provided (Braulio Bhavamitra) [86acdf4] + * puma: use environment variables (Braulio Bhavamitra) [5a853d6] + * Optionally use dotenv (Braulio Bhavamitra) [9886db3] + * api: accepts uploaded_data when create a new article (Josafá Souza Jr) [8a40c7d] + * delivery: remove unexistant dependency on orders (Braulio Bhavamitra) [129f5c4] + * database.yml: remove mysql configuration (Braulio Bhavamitra) [ed0b743] + * Finishes region definition for profiles (Gabriel Silva) [6695bf4] + * Updates category selection interface (Rodrigo Souto) [5f35895] + * Add puma configuration with Noosfero daemons (Braulio Bhavamitra) [d91cb05] + * profile: add support for tags on profiles (Rodrigo Souto) [ebe3d7f] + * stoa: temp file for db ins't working well (Braulio Bhavamitra) [0afb95d] + * suppliers: remove broken link (Braulio Bhavamitra) [af040d4] + * stoa: fix eager load with custom database (Braulio Bhavamitra) [00b90be] + * Fixes execution of /script/sample-data (Evandro Junior) [d7530f4] + * Add bin stubs from rails (Braulio Bhavamitra) [958b122] + * delivery: fix dependency on orders plugin (Braulio Bhavamitra) [44c4db1] + * delivery_plugin: enable dependent plugin (Victor Costa) [ea62612] + * Exposing Article fields (Evandro Junior) [93e8693] + * Set mime/type for folders (Evandro Junior) [e3e96a9] + * api: accept parent_id=nil to search for top level articles (Evandro Junior) [9a65d7e] + * Return correct parameters (Evandro Junior) [baa12e8] + * Eager load core and plugins (Braulio Bhavamitra) [e4a0174] + * feed_updater: no need to be executable, script/feed-updater is better (Braulio Bhavamitra) [23073d5] + * orders: remove hard limitation on debian (Braulio Bhavamitra) [14bc68c] + * Translated using Weblate (Spanish) (Carlos Duque Guasch) [9293b2d] + * Translated using Weblate (Spanish) (Carlos Duque Guasch) [0a9f54d] + * Translated using Weblate (Spanish) (Carlos Duque Guasch) [3e095bb] + * api: revert test fix that break on gitlab ci (Braulio Bhavamitra) [3a13469] + * Define default set of blocks for community angular-theme (Josafá Souza Jr) [5854d2e] + * elasticsearch: skip broken test (Braulio Bhavamitra) [f98173f] + * push_notification: fix api tests (Braulio Bhavamitra) [edea412] + * api: travis: fix api tests on newer rails (Braulio Bhavamitra) [b00d8d2] + * travis: fix cucumber build (Braulio Bhavamitra) [b1ef14d] + * Comment Gemfile (Braulio Bhavamitra) [cc953e7] + * Cleanup on lib by moving services and concerns (Braulio Bhavamitra) [04d5c50] + * selenium-webdriver: avoid requirement of geckodriver (Braulio Bhavamitra) [1e6357e] + * travis: ensure usage of supported firefox (Braulio Bhavamitra) [4c773d2] + * presenter: lib can't load app (Braulio Bhavamitra) [89c2fee] + * travis: install geckodriver (Braulio Bhavamitra) [b53e4dc] + * travis: update ruby to 2.3.3 (Braulio Bhavamitra) [07d00bd] + * application_record: fix infinite loop in development (Braulio Bhavamitra) [5bd3c2a] + * profile_test: don't use deprecated method on ruby 2.3 (Braulio Bhavamitra) [be82b20] + * adding unit tests for api_content in people block (Leandro Nunes dos Santos) [822b96b] + * return api content of members block (Leandro Nunes dos Santos) [5e7c336] + * Adds integration between moderation email to accept new users and custom fields (Victor Navarro) [f402771] + * Translated using Weblate (Spanish) (Carlos Duque Guasch) [3142025] + * Translated using Weblate (Spanish) (Carlos Duque Guasch) [230644e] + * change db/schema (Leandro Nunes dos Santos) [0963ae8] + * fix translation and identation (Leandro Nunes dos Santos) [f1896cc] + * api: return error details when validate profile (Victor Costa) [ea9fdf0] + * Add backport for ActiveModel::Errors#details (Victor Costa) [d961224] + * api: return validation errors when updating profile (Victor Costa) [24dcfcc] + * display activities of communities for visitors (Leandro Nunes dos Santos) [866f91d] + * Change order of rack cascade (Victor Costa) [026929b] + * Revert "api: do not forward 404 requests to rails" (Victor Costa) [c538544] + * api: do not forward 404 requests to rails (Victor Costa) [49c6052] + * api: specialize the return of profile endpoit (Leonardo Soares) [f8f2ac8] + * api: avoid to expose community template (Leandro Nunes dos Santos) [a034ef6] + * api: only use id parameter to get the article using find_article helper (Leandro Nunes dos Santos) [cb8bef7] + * api: remove root from all entities (Leandro Nunes dos Santos) [813e998] + * api: add count parameter to endpoints (Leandro Nunes dos Santos) [24a2740] + * api: add key parameter to search for fields other than id (Leandro Nunes dos Santos) [ce3cee3] + * Fixes orders plugin installation procedure (Gabriel Silva) [a4a3a40] + * Fixes html_safe in delivery and shopping cart plugins (Gabriel Silva) [e3b45f2] + * Fixes the loading bug when sending a scrap (Gabriel Silva) [1dd3246] + * Fixes HTML safe issues of products plugin (Gabriel Silva) [0abe3b1] + * Reorganization and small style corretions to chat (Aurélio A. Heckert) [638b930] + * Remove useless icon from chat's `user-status` (Aurélio A. Heckert) [839e4a2] + * Add option that allow person identifier to be updated (Victor Costa) [7347baa] + * Fixes product price, discount and description editing (Gabriel Silva) [13d343a] + * communities-block: add api content (Victor Costa) [2e41ec2] + * Insert custom fields before terms of use (Tallys Martins) [93f4889] + * Fixes html_safe in orders page (Gabriel Silva) [fd597cf] + * Fixes orders page layout (Gabriel Silva) [40b970f] + * adding status to api (Leandro Nunes dos Santos) [942b608] + * return tag object in tags endpoint (Leandro Nunes dos Santos) [50a1146] + * Adds class namespace to supplier_products relation (Gabriel Silva) [4a2e575] + * Installs ruby-axlsx on orders plugin installation (Gabriel Silva) [45da9b2] + * Disables the follow option for friends and members (Gabriel Silva) [8b58f18] + * adding environments/tags endpoint (Leandro Nunes dos Santos) [17268d2] + * adding environments as the default resource for environments (Leandro Nunes dos Santos) [7423eee] + * Adding /environments/people and /environments/:id/people endpoints (Leandro Nunes dos Santos) [3e6f470] + * Define default set of blocks for angular-theme (Victor Costa) [e894cec] + * menu-block: add control panel (Victor Costa) [9b5a500] + * newsletter: prevent html_safe on nil (Victor Costa) [17cd5dd] + * Notifies followers of the activity target (Gabriel Silva) [09d38c0] + * Unescapes HTML in custom form submission page (Gabriel Silva) [f8fb00e] + * Fixes translation issues (Gabriel Silva) [cb3169b] + * Takes wall_access into consideration when displaying activities (Gabriel Silva) [777a29e] + * newsletter-plugin: html safe (Rodrigo Souto) [6f213b6] + * api: pass params to api_content (Victor Costa) [a8c195a] + * pass parameter to friends block api content (Leandro Nunes dos Santos) [bef9721] + * adding params atrribute to api_content method (Leandro Nunes dos Santos) [aea391f] + * Check permissions in menu block (Victor Costa) [aba6bd2] + * Load profile tags in about page (Victor Costa) [4a947ce] + * improve menu block style (Leandro Nunes dos Santos) [b918d5c] + * adding menu block to profiles (Leandro Nunes dos Santos) [373e0fc] + * chat: typo fix (Rodrigo Souto) [244ac94] + * friends_block: return api_content with friends list (Victor Costa) [20caf68] + * chat: anonymous authentication (Rodrigo Souto) [d089dd4] + * Adds 'private content' warning to blogs and forums (Gabriel Silva) [ad4fb26] + * Orders friends list alphabetically (Gabriel Silva) [d378537] + * notification: make permanent notification throught requireInteraction property (Rodrigo Souto) [59d214d] + * Translated using Weblate (German) (Hannes Olszewski) [3d44334] + * Translated using Weblate (German) (Hannes Olszewski) [5be699e] + * Translated using Weblate (German) (Hannes Olszewski) [a9313e3] + * Translated using Weblate (German) (Hannes Olszewski) [e8d4181] + * Translated using Weblate (German) (Hannes Olszewski) [9c50a47] + * Translated using Weblate (German) (Hannes Olszewski) [b2e6676] + * Translated using Weblate (German) (Hannes Olszewski) [66cda8c] + * Translated using Weblate (German) (Hannes Olszewski) [d656f64] + * Translated using Weblate (German) (Hannes Olszewski) [fc9c232] + * Translated using Weblate (German) (Hannes Olszewski) [174f945] + * Translated using Weblate (German) (Hannes Olszewski) [629c706] + * Translated using Weblate (German) (Hannes Olszewski) [30dfa58] + * Translated using Weblate (German) (Hannes Olszewski) [0665b33] + * Translated using Weblate (German) (Hannes Olszewski) [9edc504] + * Translated using Weblate (German) (Hannes Olszewski) [becc763] + * Translated using Weblate (German) (Hannes Olszewski) [5269f44] + * Translated using Weblate (German) (Hannes Olszewski) [da84bec] + * Translated using Weblate (German) (Hannes Olszewski) [8949dad] + * Translated using Weblate (German) (Michal Čihař) [7dc305a] + * Translated using Weblate (German) (Hannes Olszewski) [76a1019] + * Translated using Weblate (German) (Hannes Olszewski) [8682d89] + * Translated using Weblate (German) (Hannes Olszewski) [3137f4a] + * Translated using Weblate (German) (Hannes Olszewski) [71e9d4e] + * Translated using Weblate (German) (Hannes Olszewski) [3f65dfd] + * Fixes HTML safe for long article leads (Gabriel Silva) [681f7fc] + * Translated using Weblate (German) (Thomas Zach) [370d6f4] + * Translated using Weblate (German) (Hannes Olszewski) [a81ef46] + * Removes catalog link from enterprise profile page (Gabriel Silva) [c58ea74] + * Fixes secret community invitations (Gabriel Silva) [add00d6] + * chat-label-counter: display counter when buddy active but chat hidden (Rodrigo Souto) [4dec5cb] + * Adds migration to update topic_creation and wall_access types (Gabriel Silva) [9d1892a] + * Changes topic_creation to integer (Gabriel Silva) [49a2083] + * Changes wall_access to integer, refactors AccessLevels (Gabriel Silva) [8db7af1] + * fix propagation of function rename list_articles -> list_contents (Italo Valcy) [0cdb404] + * Fixes HTML safe problems (Gabriel Silva) [b803b09] + * html5-video-plugin: fix encapsulated_instance method reference (Rodrigo Souto) [2832b79] + * Fixes products block style (Gabriel Silva) [925ff04] + * Fixes price composition edit form (Gabriel Silva) [bee51b0] + * chat: title and favicon notification counter (Rodrigo Souto) [ba92506] + * Tests for domain endpoint (Evandro Junior) [0e73338] + * Api endpoints for domains (Evandro Junior) [7ac8399] + * Add capability to upload top image profile (Josafá Souza Jr) [be62234] + * search: content description uses lead instead of body (Rodrigo Souto) [8f02c74] + * search: not display disabled asset (Rodrigo Souto) [f50c052] + * html-safe: category index (Rodrigo Souto) [2ddfa07] + * Add Public Access Restriction Plugin (Aurélio A. Heckert) [1b5f307] + * Add ssl true option in recaptcha_tags (matheuslacerda) [50c480f] + * Admin automatic follow after create an Organization (Karine Valença) [813b01c] + +--- + +File generated by `lib/noosfero/release.rb`. diff --git a/Gemfile b/Gemfile index 4662363509d8f7981e2b8da04ed4964463fc20ef..3eb6251f5cfdf9a5eb3fb61753fec33e8fae27bf 100644 --- a/Gemfile +++ b/Gemfile @@ -19,7 +19,7 @@ gem 'acts-as-taggable-on', '~> 3.5' gem 'rails_autolink', '~> 1.1.5' gem 'ruby-feedparser', '~> 0.7' gem 'daemons', '~> 1.1' -gem 'nokogiri', '~> 1.6.0' +gem 'nokogiri', (if RUBY_VERSION >= '2.4.0' then '~> 1.7.0' else '~> 1.6.0' end) gem 'will_paginate', '~> 3.0.7' gem 'pothoven-attachment_fu', '~> 3.2.16' gem 'delayed_job' @@ -63,9 +63,10 @@ group :development, :test do gem 'spring' end +gem 'rspec', '~> 3.3' +gem 'rspec-rails', '~> 3.2' + group :test do - gem 'rspec', '~> 3.3', require: false - gem 'rspec-rails', '~> 3.2', require: false gem 'mocha', '~> 1.1.0', :require => false gem 'test-unit' if RUBY_VERSION >= '2.2.0' gem 'minitest' diff --git a/Rakefile b/Rakefile index d88acb7aec0db5065692585ef5b2915e7d44aef4..439af8b347ff0aadc2e5a22573c07dc5da7d9a4f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,9 +1,6 @@ #!/usr/bin/env rake -# Add your own tasks in files placed in lib/tasks ending in .rake, -# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. - -require File.expand_path('../config/application', __FILE__) +require_relative 'config/application' Noosfero::Application.load_tasks diff --git a/app/api/app.rb b/app/api/app.rb index 1b74a7028dfba49d36e6abbb463285b4b5736174..514e1ac9e3a5bfdff805486983596b7abbb73c07 100644 --- a/app/api/app.rb +++ b/app/api/app.rb @@ -56,6 +56,7 @@ mount V1::Profiles mount V1::Activities mount V1::Roles mount V1::Domains + mount V1::Settings # hook point which allow plugins to add Grape::API extensions to Api::App #finds for plugins which has api mount points classes defined (the class should extends Grape::API) diff --git a/app/api/entities.rb b/app/api/entities.rb index 3128fc6cb333648adab250f4dbe224d04f8371cf..78416d0eb9f602f2a2c3a58d9487d9c43e90ffe0 100644 --- a/app/api/entities.rb +++ b/app/api/entities.rb @@ -80,13 +80,20 @@ end expose :parent, :using => CategoryBase, if: { parent: true } expose :children, :using => CategoryBase, if: { children: true } expose :image, :using => Image - expose :display_color + expose :display_color end class Region < Category expose :parent_id end + class BlockDefinition < Entity + expose :description + expose :short_description + expose :pretty_name, as: :name + expose :name, as: :type + end + class Block < Entity expose :id, :type, :settings, :position, :enabled expose :mirror, :mirror_block_id, :title @@ -97,6 +104,9 @@ expose :permissions do |block, options| Entities.permissions_for_entity(block, options[:current_person], :allow_edit?) end expose :images, :using => Image + expose :definition do |block, options| + BlockDefinition.represent(block.class) + end end class Box < Entity @@ -110,27 +120,26 @@ class Profile < Entity expose :identifier, :name, :id expose :created_at, :format_with => :timestamp expose :updated_at, :format_with => :timestamp - expose :additional_data do |profile, options| - hash ={} - profile.public_values.each do |value| - hash[value.custom_field.name]=value.value - end - profile.public_fields.each do |field| - hash[field] = profile.send(field.to_sym) - end + expose :additional_data do |profile, options| + hash = {} + profile.environment.send("all_custom_#{profile.type.downcase}_fields").each do |field, settings| + if settings['active'].to_s == 'true' + field_privacy = profile.fields_privacy[field] || profile.fields_privacy[field.to_sym] + value = field_privacy == 'public' ? :anonymous : :private_content + if Entities.can_display_profile_field?(profile, options, { :field => field, permission: value }) + hash[field] = profile.send('custom_field_value', field) + end + end + end - private_values = profile.custom_field_values - profile.public_values - private_values.each do |value| - if Entities.can_display_profile_field?(profile,options) - hash[value.custom_field.name]=value.value - end - end hash + end expose :image, :using => Image expose :top_image, :using => Image expose :region, :using => Region + expose :tag_list expose :type expose :custom_header expose :custom_footer @@ -138,6 +147,9 @@ expose :layout_template expose :permissions do |profile, options| Entities.permissions_for_entity(profile, options[:current_person], :allow_post_content?, :allow_edit?, :allow_destroy?) + end + expose :theme do |profile, options| + profile.theme || profile.environment.theme end end @@ -179,7 +191,7 @@ expose :admins, :if => lambda { |community, options| community.display_info_to? options[:current_person]} do |community, options| community.admins.map{|admin| {"name"=>admin.name, "id"=>admin.id, "username" => admin.identifier}} end expose :categories, :using => Category - expose :members_count + expose :members_count, :closed expose :members, :if => lambda {|community, options| Entities.expose_optional_field?(:members, options)} end @@ -307,10 +319,12 @@ expose :settings, if: lambda { |instance, options| options[:is_admin] } expose :permissions, if: lambda { |environment, options| options[:current_person].present? } do |environment, options| environment.permissions_for(options[:current_person]) end + expose :theme end class Tag < Entity expose :name + expose :taggings_count, as: :count end class Activity < Entity @@ -333,6 +347,9 @@ class Role < Entity expose :id expose :name expose :key + expose :assigned do |role, options| + (options[:person_roles] || []).include?(role) + end end class AbuseReport < Entity @@ -355,5 +372,16 @@ type_map = {Profile => ::Profile, Environment => ::Environment}.find {|k,v| domain.owner.kind_of?(v)} type_map.first.represent(domain.owner, options) unless type_map.nil? end end + + class Response < Entity + expose :success + expose :code + expose :message + end + + class Setting < Entity + expose :available_blocks, :using => BlockDefinition + end + end end diff --git a/app/api/helpers.rb b/app/api/helpers.rb index 81f6f6094fe3f5f3a7517910d53b02fdb03d1704..0fd7cf7c1a7746729693aa4696aa65799c2cf732 100644 --- a/app/api/helpers.rb +++ b/app/api/helpers.rb @@ -134,7 +134,7 @@ article.created_by= current_person article.profile = asset if !article.save - render_api_errors!(article.errors.full_messages) + render_model_errors!(article.errors) end present_partial article, :with => Entities::Article end @@ -181,7 +181,7 @@ task.target_id = asset.id task.target_type = 'Profile' if !task.save - render_api_errors!(task.errors.full_messages) + render_model_errors!(task.errors) end present_partial task, :with => Entities::Task end @@ -215,17 +215,20 @@ ########################### # Activities # ########################### - def find_activities(asset, method_or_relation = 'activities') + def find_activities(asset, method_or_relation = 'tracked_notifications') not_found! if asset.blank? || asset.secret || !asset.visible forbidden! if !asset.display_private_info_to?(current_person) - - activities = select_filtered_collection_of(asset, method_or_relation, params) - activities = activities.map(&:activity) + if method_or_relation == 'activities' + activities = select_filtered_collection_of(asset, method_or_relation, params) + activities = activities.map(&:activity) + else + activities = select_filtered_collection_of(asset, method_or_relation, params) + end activities end - def present_activities_for_asset(asset, method_or_relation = 'activities') + def present_activities_for_asset(asset, method_or_relation = 'tracked_notifications') tasks = find_activities(asset, method_or_relation) present_activities(tasks) end @@ -318,6 +321,11 @@ objects = by_categories(objects, params) objects = objects.where(conditions).where(timestamp).reorder(order) + if params[:search].present? + asset = objects.model.name.underscore.pluralize + objects = find_by_contents(asset, object, objects, params[:search])[:results] + end + params[:page] ||= 1 params[:per_page] ||= limit paginate(objects) @@ -355,7 +363,7 @@ attrs end ########################################## - # error helpers # + # response helpers # ########################################## def not_found! @@ -389,20 +397,23 @@ def not_allowed! render_api_error!(_('Method Not Allowed'), Api::Status::METHOD_NOT_ALLOWED) end - # javascript_console_message is supposed to be executed as console.log() - def render_api_error!(user_message, status, log_message = nil, javascript_console_message = nil) - message_hash = {'message' => user_message, :code => status} - message_hash[:javascript_console_message] = javascript_console_message if javascript_console_message.present? - log_msg = "#{status}, User message: #{user_message}" - log_msg = "#{log_message}, #{log_msg}" if log_message.present? - log_msg = "#{log_msg}, Javascript Console Message: #{javascript_console_message}" if javascript_console_message.present? - logger.error log_msg unless Rails.env.test? + def render_api_error!(user_message, status = Api::Status::BAD_REQUEST) + message_hash = {'message' => user_message} + log_message = "#{status}, User message: #{user_message}" + logger.error log_message unless Rails.env.test? error!(message_hash, status) end - def render_api_errors!(messages) - messages = messages.to_a if messages.class == ActiveModel::Errors - render_api_error!(messages.join(','), Api::Status::BAD_REQUEST) + def render_model_errors!(active_record_errors) + message_hash = {} + if active_record_errors.details + message_hash[:errors] = active_record_errors.details + message_hash[:errors].each do |field, errors| + full_messages = active_record_errors.full_messages_for(field) + errors.each_with_index {|error, i| error[:full_message] = full_messages[i] } + end + end + error!(message_hash, Api::Status::UNPROCESSABLE_ENTITY) end protected @@ -523,6 +534,12 @@ def period(from_date, until_date) begin_period = from_date.nil? ? Time.at(0).to_datetime : from_date end_period = until_date.nil? ? DateTime.now : until_date begin_period..end_period + end + + def settings(owner) + blocks = owner.available_blocks(current_person) + settings = {:available_blocks => blocks} + settings end end end diff --git a/app/api/status.rb b/app/api/status.rb index d19b25fe1801cb418bfdd060e6551c72c5f13cde..2ac9323f1903195c4db71a4604860105055531fc 100644 --- a/app/api/status.rb +++ b/app/api/status.rb @@ -7,11 +7,13 @@ UNAUTHORIZED = 401 FORBIDDEN = 403 NOT_FOUND = 404 METHOD_NOT_ALLOWED = 405 + UNPROCESSABLE_ENTITY = 422 OK = 200 CREATED = 201 # Noosfero API Status DEPRECATED = 299 + INVITATION_SENT_TO_BE_PROCESSED = 298 end end diff --git a/app/api/v1/activities.rb b/app/api/v1/activities.rb index 4d9da55879cffd3273029b2099116eed740b34d2..a62306f29a60ced314358dafdbb3a3b4407c1444 100644 --- a/app/api/v1/activities.rb +++ b/app/api/v1/activities.rb @@ -6,7 +6,12 @@ resource :profiles do get ':id/activities' do profile = environment.profiles.find_by id: params[:id] - present_activities_for_asset(profile) + present_activities_for_asset(profile, 'activities') + end + + get ':id/network_activities' do + profile = environment.profiles.find_by id: params[:id] + present_activities_for_asset(profile, 'tracked_notifications') end end end diff --git a/app/api/v1/articles.rb b/app/api/v1/articles.rb index faa5754771bc3cd78534f43316b135fc8e339c51..34ac7953d731c754f72df0659fce6e5c97572dca 100644 --- a/app/api/v1/articles.rb +++ b/app/api/v1/articles.rb @@ -125,7 +125,7 @@ begin vote = Vote.new(:voteable => article, :voter => current_person, :vote => value) {:vote => vote.save!} rescue ActiveRecord::RecordInvalid => e - render_api_error!(e.message, Api::Status::BAD_REQUEST) + render_model_errors!(vote.errors) end end @@ -212,7 +212,7 @@ suggest_article.target = parent_article.profile suggest_article.requestor = current_person unless suggest_article.save - render_api_errors!(suggest_article.article_object.errors.full_messages) + render_model_errors!(suggest_article.article_object.errors) end present_partial suggest_article, :with => Entities::Task end diff --git a/app/api/v1/blocks.rb b/app/api/v1/blocks.rb index 7234b375b2fd26ea4c5b0d397b0336aad1a86da8..5bd95ae8a8b7d7765ae88fcc9b9ec1f37ac3d709 100644 --- a/app/api/v1/blocks.rb +++ b/app/api/v1/blocks.rb @@ -2,6 +2,7 @@ module Api module V1 class Blocks < Grape::API + resource :blocks do get ':id' do block = Block.find(params["id"]) @@ -15,30 +16,8 @@ return forbidden! unless block.allow_edit?(current_person) block.update_attributes!(asset_with_images(params[:block])) present_partial block, :with => Entities::Block, display_api_content: true, current_person: current_person, api_content_params: params.except("id") end + end - patch do - error = nil - blocks = Block.transaction do - params["blocks"].map do |block_params| - block = Block.find(block_params["id"]) - return forbidden! unless block.allow_edit?(current_person) - begin - block.update_attributes!(asset_with_images(block_params)) - rescue => e - error = e - raise ActiveRecord::Rollback - end - block - end - end - if error.nil? - present_partial blocks, :with => Entities::Block, current_person: current_person - else - error! error.message, 500 - end - end - end end - end end diff --git a/app/api/v1/communities.rb b/app/api/v1/communities.rb index ea4350b81f51987631b646d767922d16ad5c28cb..5a5fb68d223a261f716e02e842d9942519f34cfa 100644 --- a/app/api/v1/communities.rb +++ b/app/api/v1/communities.rb @@ -41,7 +41,7 @@ community = Community.new(params[:community]) end if !community.save - render_api_errors!(community.errors.full_messages) + render_model_errors!(community.errors) end present_partial community, :with => Entities::Community, :current_person => current_person @@ -51,6 +51,29 @@ get ':id' do community = profiles_for_person(environment.communities, current_person).find_by_id(params[:id]) not_found! unless community.present? present_partial community, :with => Entities::Community, :current_person => current_person, :params => params + end + + + + desc 'Send invitations of users to community' do + detail 'The invitation must be provided by a user logged user with permission' + params Entities::Response.documentation + success Entities::Response + named 'CommunityInvite' + end + post ':id/invite' do + authenticate! + community = profiles_for_person(environment.communities, current_person).find_by_id(params[:id]) + not_found! unless community.present? + forbidden! unless community.allow_invitation_from?(current_person) + Delayed::Job.enqueue InvitationJob.new(current_person.id, params[:contacts], '', community.id, nil, I18n.locale) + msg = { + :success => true, + :message => _('Your invitation was registered. The community administrators are reviewing your solicitation.'), + :code => Api::Status::INVITATION_SENT_TO_BE_PROCESSED + } + + present msg, :with => Entities::Response end end diff --git a/app/api/v1/people.rb b/app/api/v1/people.rb index 7d09d034e797369d0042dbb36694a0e9f9bbab05..dc9bfba20b96c38b591a15d68a3f917e5e128e82 100644 --- a/app/api/v1/people.rb +++ b/app/api/v1/people.rb @@ -79,7 +79,7 @@ begin user.signup! rescue ActiveRecord::RecordInvalid - render_api_errors!(user.errors.full_messages) + render_model_errors!(user.errors) end present_partial user.person, :with => Entities::Person, :current_person => user.person @@ -89,8 +89,44 @@ desc "Return the person friends" get ':id/friends' do person = environment.people.visible.find_by(id: params[:id]) return not_found! if person.blank? - friends = person.friends.visible + friends = select_filtered_collection_of(person, person.friends.visible, params) present_partial friends, :with => Entities::Person, :current_person => current_person + end + + desc "Return the person friend by id" + get ':id/friends/:friend_id' do + person = environment.people.visible.find_by(id: params[:id]) + friend = person.friends.visible.find_by(id: params[:friend_id]) + return not_found! if friend.blank? + present(friend, :with => Entities::Person, :current_person => current_person) + end + + desc "Add a new friend" + post ':id/friends' do + authenticate! + person = environment.people.visible.find_by(id: params[:id]) + return not_found! if person.blank? + return bad_request!(('You are already a friend of %s.').html_safe % person.name) if current_person.memberships.include?(person) + add_friend = AddFriend.new(:person => current_person, :friend => person) + begin + add_friend.save! + present({ message: 'WAITING_APPROVAL' }) + rescue ActiveRecord::RecordInvalid + render_model_errors!(add_friend.errors) + end + end + + desc "Remove a friend" + delete ':id/friends' do + authenticate! + person = environment.people.visible.find_by(id: params[:id]) + return not_found! if person.blank? + begin + current_person.remove_friend(person); + present({ message: 'Friend successfuly removed' }) + rescue ActiveRecord::RecordInvalid + bad_request! + end end desc "Return the person permissions on other profiles" diff --git a/app/api/v1/profiles.rb b/app/api/v1/profiles.rb index 1298f9f95503b1409c4321c6b53e58d88025a51f..e8f11dd2384b5d905564beaee8a53e933138cf17 100644 --- a/app/api/v1/profiles.rb +++ b/app/api/v1/profiles.rb @@ -11,7 +11,7 @@ profiles = profiles.by_location(params) # Must be the last. May return Exception obj. present profiles, :with => Entities::Profile, :current_person => current_person end - get ':id' do + get ':id', requirements: { id: /#{Noosfero.identifier_format}/ } do profiles = environment.profiles profiles = profiles.visible key = params[:key].to_s == "identifier" ? :identifier : :id @@ -20,7 +20,7 @@ profile = profiles.find_by key => params[:id] if profile type_map = { - Person => Entities::Person, + Person => Entities::Person, Community => Entities::Community, Enterprise => Entities::Enterprise }[profile.class] || Entities::Profile @@ -30,7 +30,7 @@ else not_found! end end - + desc "Update profile information" post ':id' do authenticate! @@ -41,7 +41,7 @@ profile_params = asset_with_image(params[:profile]) profile.update_attributes!(asset_with_custom_image(:top_image, profile_params)) present profile, :with => Entities::Profile, :current_person => current_person rescue ActiveRecord::RecordInvalid - render_api_error!(profile.errors.details, Api::Status::BAD_REQUEST) + render_model_errors!(profile.errors) end end @@ -53,7 +53,7 @@ not_found! if profile.blank? if profile.allow_destroy?(current_person) - profile.destroy + present({ success: profile.destroy }) else forbidden! end diff --git a/app/api/v1/roles.rb b/app/api/v1/roles.rb index fd3f33f8f751fe69d49700cde7fb9cd737492cd8..e8650af969615655608fd1bb570905102555033d 100644 --- a/app/api/v1/roles.rb +++ b/app/api/v1/roles.rb @@ -14,9 +14,27 @@ get do profile = environment.profiles.find(params[:profile_id]) return forbidden! unless profile.kind_of?(Organization) roles = Profile::Roles.organization_roles(profile.environment.id, profile.id) - present_partial paginate(roles), with: Entities::Role + person_roles = [] + if params[:person_id].present? + person = environment.people.find(params[:person_id]) + person_roles = person.role_assignments.where(resource: profile).joins(:role).map(&:role) + end + present_partial paginate(roles), with: Entities::Role, person_roles: person_roles end - + + resource :assign do + post do + profile = environment.profiles.find(params[:profile_id]) + return forbidden! unless profile.kind_of?(Organization) + + person = environment.people.find(params[:person_id]) + profile.affiliate(person, Role.find(params[:role_ids])) if params[:role_ids].present? + profile.disaffiliate(person, Role.find(params[:remove_role_ids])) if params[:remove_role_ids].present? + person_roles = person.role_assignments.where(resource: profile).joins(:role).map(&:role) + present_partial paginate(person_roles), with: Entities::Role + end + end + end end end diff --git a/app/api/v1/search.rb b/app/api/v1/search.rb index 8594d10238008309aa1c25c93b603f77d3f90315..c7c00f97f2a6e923238fa474dbd8dff9c2cb0662 100644 --- a/app/api/v1/search.rb +++ b/app/api/v1/search.rb @@ -26,7 +26,7 @@ search_result = find_by_contents(asset, context, scope, query, {:page => 1}, options) articles = search_result[:results] - + status Api::Status::DEPRECATED present_articles(articles) end end diff --git a/app/api/v1/session.rb b/app/api/v1/session.rb index a885f4c64d728b2f603bd9c35b081dce7ffa51d7..72fb69afd8b64ab6c0ea24ad2f1af0c8e7ab5861 100644 --- a/app/api/v1/session.rb +++ b/app/api/v1/session.rb @@ -15,6 +15,10 @@ # POST http://localhost:3000/api/v1/login?login=adminuser&password=admin post "/login" do begin user ||= User.authenticate(params[:login], params[:password], environment) + @plugins.each do |plugin| + user ||= plugin.alternative_authentication + break unless user.nil? + end rescue User::UserNotActivated => e render_api_error!(e.message, Api::Status::UNAUTHORIZED) end diff --git a/app/api/v1/settings.rb b/app/api/v1/settings.rb new file mode 100644 index 0000000000000000000000000000000000000000..e4063731ba1e76b32153fc1ec1afcceaf6af74f5 --- /dev/null +++ b/app/api/v1/settings.rb @@ -0,0 +1,29 @@ +module Api + module V1 + + class Settings < Grape::API + + kinds = %w[profile environment] + kinds.each do |kind| + resource kind.pluralize.to_sym do + segment "/:#{kind}_id" do + resource :settings do + + get do + owner = kind=='environment' ? Environment.find(params["#{kind}_id"]) : environment.send(kind.pluralize).find(params["#{kind}_id"]) + present_partial settings(owner), :with => Entities::Setting, current_person: current_person + end + + get 'available_blocks' do + owner = kind=='environment' ? Environment.find(params["#{kind}_id"]) : environment.send(kind.pluralize).find(params["#{kind}_id"]) + present_partial settings(owner)[:available_blocks], :with => Entities::BlockDefinition, current_person: current_person + end + + end + end + end + end + + end + end +end diff --git a/app/api/v1/tags.rb b/app/api/v1/tags.rb index 6b832393840515b0ed43f627a4e64b36c887131b..ca6b9dbd77b2061e15d84d4f5582c7a746b90e46 100644 --- a/app/api/v1/tags.rb +++ b/app/api/v1/tags.rb @@ -5,7 +5,7 @@ resource :articles do resource ':id/tags' do get do article = find_article(environment.articles, {:id => params[:id]}) - present_partial article.tag_list, {} + present_partial article.tags, :with => Entities::Tag end desc "Add a tag to an article" @@ -14,16 +14,26 @@ authenticate! article = find_article(environment.articles, {:id => params[:id]}) article.tag_list=params[:tags] article.save - present_partial article.tag_list, {} + present_partial article.tags, :with => Entities::Tag end end end - resource :environment do - desc 'Return the tag counts for this environment' - get '/tags' do - status Api::Status::DEPRECATED - present_partial environment.tag_counts, {} + resource :profiles do + resource ':id/tags' do + get do + profile = environment.profiles.find params[:id] + present_partial profile.tags, :with => Entities::Tag + end + + desc "Add a tag to a profile" + post do + authenticate! + profile = environment.profiles.find params[:id] + profile.tag_list=params[:tags] + profile.save + present_partial profile.tags, :with => Entities::Tag + end end end @@ -31,13 +41,13 @@ resource :environments do resource ':id/tags' do get do local_environment = Environment.find(params[:id]) - present_partial local_environment.articles.tag_counts, {} + present_partial local_environment.tags, :with => Entities::Tag end end desc 'Return the tag counts for this environment' get '/tags' do - present_partial environment.articles.tag_counts, {} + present_partial environment.tags, :with => Entities::Tag end end end diff --git a/app/api/v1/users.rb b/app/api/v1/users.rb index a5a63032c912bd0711741f489f694edfcb5e5f65..cca564d64fc489305b156f4abe746a83ff10feec 100644 --- a/app/api/v1/users.rb +++ b/app/api/v1/users.rb @@ -36,6 +36,19 @@ end present output end + patch ":id" do + authenticate! + begin + current_person.user.change_password!(params[:current_password], + params[:new_password], + params[:new_password_confirmation]) + present({ success: true }) + rescue Exception + render_model_errors!(current_person.user.errors) + end + + end + end end diff --git a/app/concerns/find_by_contents.rb b/app/concerns/find_by_contents.rb index 2c1eab288993da8728e7614ec4f1ad6b6fcc6426..07a0864b6d883123776d0f0596211214125a693b 100644 --- a/app/concerns/find_by_contents.rb +++ b/app/concerns/find_by_contents.rb @@ -18,6 +18,7 @@ end def find_by_contents(asset, context, scope, query, paginate_options={:page => 1}, options={}) scope = scope.with_templates(options[:template_id]) unless options[:template_id].blank? + scope = scope.tagged_with(options[:tag]) unless options[:tag].blank? search = plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) #register_search_term(query, scope.count, search[:results].count, context, asset) search diff --git a/app/controllers/admin/environment_design_controller.rb b/app/controllers/admin/environment_design_controller.rb index 0e033467c0ad770d9c70b73fb75925515f5ad6bb..e4d56ab941819d59421de6c26794a38470b713ac 100644 --- a/app/controllers/admin/environment_design_controller.rb +++ b/app/controllers/admin/environment_design_controller.rb @@ -3,8 +3,7 @@ protect 'edit_environment_design', :environment def available_blocks - @available_blocks ||= [ ArticleBlock, LoginBlock, RecentDocumentsBlock, EnterprisesBlock, CommunitiesBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, HighlightsBlock, CategoriesBlock, RawHTMLBlock, TagsBlock ] - @available_blocks += plugins.dispatch(:extra_blocks, :type => Environment) + boxes_holder.available_blocks(user) end end diff --git a/app/controllers/my_profile/cms_controller.rb b/app/controllers/my_profile/cms_controller.rb index 35ae09d988c175cb9557b853638f642a738534bd..07d55d632dba2e8f126cd2a65fbdb2c0fcb2cabe 100644 --- a/app/controllers/my_profile/cms_controller.rb +++ b/app/controllers/my_profile/cms_controller.rb @@ -4,12 +4,7 @@ protect 'edit_profile', :profile, :only => [:set_home_page] include ArticleHelper include CategoriesHelper - - def search_tags - arg = params[:term].downcase - result = Tag.where('name ILIKE ?', "%#{arg}%").limit(10) - render :text => prepare_to_token_input_by_label(result).to_json, :content_type => 'application/json' - end + include SearchTags def self.protect_if(*args) before_filter(*args) do |c| @@ -198,9 +193,7 @@ def upload_files @uploaded_files = [] @article = @parent = check_parent(params[:parent_id]) @target = @parent ? ('/%s/%s' % [profile.identifier, @parent.full_name]) : '/%s' % profile.identifier - if @article - record_coming - end + record_coming if request.post? && params[:uploaded_files] params[:uploaded_files].each do |file| unless file == '' @@ -258,8 +251,10 @@ render_categories 'article' end def search_communities_to_publish - scope = user.memberships.distinct(false).group("profiles.id") - render :text => find_by_contents(:profiles, environment, scope, params['q'], {:page => 1}, {:fields => ['name']})[:results].map {|community| {:id => community.id, :name => community.name} }.to_json + scope = user.memberships.distinct(false) + results = find_by_contents(:profiles, environment, scope, params['q'], {:page => 1}, {:fields => ['name']})[:results] + render :text => results.map {|community| {:id => community.id, :name => community.name} } + .uniq {|c| c[:id] }.to_json end def publish diff --git a/app/controllers/my_profile/maps_controller.rb b/app/controllers/my_profile/maps_controller.rb index a68da78ca08519392db444d30279ebdef87f097a..e6e9fc216a174783bcd21903064e9d063f8941f8 100644 --- a/app/controllers/my_profile/maps_controller.rb +++ b/app/controllers/my_profile/maps_controller.rb @@ -30,10 +30,6 @@ end end end - def google_map - render :partial => 'google_map.js' - end - def search_city render :json => MapsHelper.search_city(params[:term]) end diff --git a/app/controllers/my_profile/profile_design_controller.rb b/app/controllers/my_profile/profile_design_controller.rb index 69386cbd012c59bdf58d4727c3c8ae9221d17273..99257564a9f946e0a158031c70104cf8371374a5 100644 --- a/app/controllers/my_profile/profile_design_controller.rb +++ b/app/controllers/my_profile/profile_design_controller.rb @@ -24,41 +24,7 @@ end end def available_blocks - blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock, HighlightsBlock, MenuBlock ] - - blocks += plugins.dispatch(:extra_blocks) - - # blocks exclusive to people - if profile.person? - blocks << FavoriteEnterprisesBlock - blocks << CommunitiesBlock - blocks << EnterprisesBlock - blocks += plugins.dispatch(:extra_blocks, :type => Person) - end - - # blocks exclusive to communities - if profile.community? - blocks += plugins.dispatch(:extra_blocks, :type => Community) - end - - # blocks exclusive for enterprises - if profile.enterprise? - blocks << DisabledEnterpriseMessageBlock - blocks << HighlightsBlock - blocks << FansBlock - blocks += plugins.dispatch(:extra_blocks, :type => Enterprise) - end - - # block exclusive to profiles that have blog - if profile.has_blog? - blocks << BlogArchivesBlock - end - - if @user_is_admin - blocks << RawHTMLBlock - end - - blocks + profile.available_blocks(user) end def update_categories diff --git a/app/controllers/my_profile/profile_editor_controller.rb b/app/controllers/my_profile/profile_editor_controller.rb index 4ad86f5801a20c4f2039ee75d8cb15d7ab2dc2c4..6f120f00a922836a1869183f00aef42fc693239b 100644 --- a/app/controllers/my_profile/profile_editor_controller.rb +++ b/app/controllers/my_profile/profile_editor_controller.rb @@ -11,6 +11,8 @@ helper_method :has_welcome_page helper CustomFieldsHelper include CategoriesHelper + include SearchTags + def index @pending_tasks = Task.to(profile).pending.without_spam @show_appearance_option = user.is_admin?(environment) || environment.enabled?('enable_appearance') diff --git a/app/controllers/public/account_controller.rb b/app/controllers/public/account_controller.rb index a40da99dc0d8168f09b83ff9a28f9100d8f55407..e637c3b5e7f19fb4241e5768a01e88f269c282b0 100644 --- a/app/controllers/public/account_controller.rb +++ b/app/controllers/public/account_controller.rb @@ -116,7 +116,8 @@ @block_bot = true session[:may_be_a_bot] = true else if session[:may_be_a_bot] - return false unless captcha_verify :model=>@user, :message=>_('Captcha (the human test)') + return false unless verify_recaptcha :model=>@user, :message=>_('Captcha (the human test)') + session[:may_be_a_bot] = false end @user.community_to_join = session[:join] @user.signup! diff --git a/app/controllers/public/profile_controller.rb b/app/controllers/public/profile_controller.rb index 01d838b6e1cff69110ccfd9228f7229931e6c6db..2f33371349adcd5380eff99977370cefd256a5b8 100644 --- a/app/controllers/public/profile_controller.rb +++ b/app/controllers/public/profile_controller.rb @@ -48,7 +48,7 @@ def content_tagged @tag = params[:id] @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_#{profile.id.to_s}_page_#{params[:npage]}" if is_cache_expired?(@tag_cache_key) - @tagged = profile.tagged_with(@tag).paginate(:per_page => 20, :page => params[:npage]) + @tagged = profile.articles.tagged_with(@tag).paginate(:per_page => 20, :page => params[:npage]) end end @@ -141,7 +141,7 @@ if user redirect_to :controller => 'profile', :action => 'join' else - redirect_to :controller => '/account', :action => 'login' + redirect_to :controller => '/account', :action => 'login', :return_to => profile.url end end diff --git a/app/controllers/public/search_controller.rb b/app/controllers/public/search_controller.rb index cc69fb7c9e606c99501391264c25a86ff0473771..627557581e22c039bf2ac31c2c0df0e6a3c7dadc 100644 --- a/app/controllers/public/search_controller.rb +++ b/app/controllers/public/search_controller.rb @@ -10,6 +10,7 @@ before_filter :sanitize_params before_filter :redirect_asset_param, :except => [:assets, :suggestions] before_filter :load_category, :except => :suggestions + before_filter :load_tag, :except => :suggestions before_filter :load_search_assets, :except => :suggestions before_filter :load_query, :except => :suggestions before_filter :load_order, :except => :suggestions @@ -134,16 +135,16 @@ def tags @tags_cache_key = "tags_env_#{environment.id.to_s}" if is_cache_expired?(@tags_cache_key) - @tags = environment.tag_counts + @tags = environment.environment_tags end end def tag - @tag = params[:tag] tag_str = @tag.kind_of?(Array) ? @tag.join(" ") : @tag.to_str @tag_cache_key = "tag_#{CGI.escape(tag_str)}_env_#{environment.id.to_s}_page_#{params[:npage]}" if is_cache_expired?(@tag_cache_key) - @searches[@asset] = {:results => environment.articles.tagged_with(@tag).paginate(paginate_options)} + send(:index) + @asset = :tag end end @@ -174,6 +175,10 @@ @query = params[:query] || '' @empty_query = @category.nil? && @query.blank? end + def load_tag + @tag = params[:tag] + end + def load_category if params[:category_path].blank? render_not_found if params[:action] == 'category_index' @@ -209,7 +214,7 @@ @searching = {} @titles = {} @enabled_searches.each do |key, name| @titles[key] = _(name) - @searching[key] = params[:action] == 'index' || params[:action] == key.to_s + @searching[key] = params[:action] == 'index' || params[:action] == 'tag' || params[:action] == key.to_s end @names = @titles if @names.nil? end @@ -248,7 +253,9 @@ { :per_page => limit, :page => page } end def full_text_search - @searches[@asset] = find_by_contents(@asset, environment, @scope, @query, paginate_options, {:category => @category, :filter => @order, :template_id => params[:template_id]}) + @searches[@asset] = find_by_contents(@asset, environment, @scope, @query, paginate_options, + {:category => @category, :tag => @tag, :filter => @order, :template_id => params[:template_id], + :facets => params[:facets], :periods => params[:periods]}) end private diff --git a/app/helpers/action_tracker_helper.rb b/app/helpers/action_tracker_helper.rb index 8e663494cfbe02f2565895d82ae2bea926d18f9a..6fffe0ac885b80e0745f27a1d58a9e8b2995c982 100644 --- a/app/helpers/action_tracker_helper.rb +++ b/app/helpers/action_tracker_helper.rb @@ -61,15 +61,19 @@ class: 'more', onclick: "this.parentNode.className+=' show-all'" end + tag(:br, style: 'clear: both') end - def reply_scrap_description ta + def leave_scrap_description ta _('sent a message to %{receiver}: <br /> "%{message}"') % { receiver: link_to(ta.get_receiver_name, ta.get_receiver_url), message: auto_link_urls(ta.get_content) } end - alias :leave_scrap_description :reply_scrap_description - alias :reply_scrap_on_self_description :reply_scrap_description + def reply_scrap_on_self_description ta + _('replied to a scrap from %{receiver}: <br /> "%{message}"') % { + receiver: link_to(ta.get_receiver_name, ta.get_receiver_url), + message: auto_link_urls(ta.get_content) + } + end def leave_scrap_to_self_description ta _('wrote: <br /> "%{text}"') % { diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a3619c372a2dc47442d657a2e2a8589782ea4e2e..9b47852a2882158401fb968f15330699bea45b7f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -288,7 +288,7 @@ from_theme_include(nil, template, options) end def env_theme_include(template, options = {}) - from_theme_include(environment.theme, template, options) + from_theme_include(session[:theme] || environment.theme, template, options) end def from_theme_include(theme, template, options = {}) @@ -496,7 +496,7 @@ content_tag('div', label_html + control_html, :class => 'formfieldline' ) end - (field_helpers - %w(hidden_field)).each do |selector| + (field_helpers - %i(hidden_field)).each do |selector| src = <<-END_SRC def #{selector}(field, *args, &proc) begin @@ -907,18 +907,34 @@ count = user ? Task.to(user).pending.count : -1 if count > 0 pending_tasks_count = link_to(count.to_s, user.tasks_url, :id => 'pending-tasks-count', :title => _("Manage your pending tasks")) end + user_identifier = "<i style='background-image:url(#{user.profile_custom_icon(gravatar_default)})'></i><strong>#{user.identifier}</strong>" + welcome_link = link_to(user_identifier.html_safe, user.public_profile_url, :id => "homepage-link", :title => _('Go to your homepage')) welcome_span = _("<span class='welcome'>Welcome,</span> %s") % welcome_link.html_safe + ctrl_panel_icon = '<i class="icon-menu-ctrl-panel"></i>' ctrl_panel_section = '<strong>' + ctrl_panel_icon + _('Control panel') + '</strong>' ctrl_panel_link = link_to(ctrl_panel_section.html_safe, user.admin_url, :class => 'ctrl-panel', :title => _("Configure your personal account and content")) + logout_icon = '<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>' logout_link = link_to(logout_icon.html_safe, { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) + + plugins_items = @plugins.dispatch(:user_menu_items, user).collect { |content| instance_eval(&content) } + + items = [ + welcome_span.html_safe, + *plugins_items, + render_environment_features(:usermenu).html_safe, + admin_link.html_safe, + manage_enterprises, + manage_communities, + ctrl_panel_link.html_safe, + pending_tasks_count.html_safe, + logout_link.html_safe + ] join_result = safe_join( - [welcome_span.html_safe, render_environment_features(:usermenu).html_safe, admin_link.html_safe, - manage_enterprises, manage_communities, ctrl_panel_link.html_safe, - pending_tasks_count.html_safe, logout_link.html_safe], "") + items, "") join_result end @@ -968,7 +984,7 @@ source_url = link_to(page.source_name.blank? ? page.source : page.source_name, page.source) elsif page.reference_article source_url = link_to(page.reference_article.profile.name, page.reference_article.url) end - content_tag(:div, (_('Source: %s') % source_url).html_safe, :id => 'article-source') unless source_url.nil? + content_tag(:div, _('Source: %s').html_safe % source_url.html_safe, :id => 'article-source') unless source_url.nil? end def task_information(task, params = {}) diff --git a/app/helpers/content_viewer_helper.rb b/app/helpers/content_viewer_helper.rb index a9b8f9020141d358a267f14d6c2b94215c4230fb..9ea5bac346a1a12ff67c40b4f574a8fcdc8ed407 100644 --- a/app/helpers/content_viewer_helper.rb +++ b/app/helpers/content_viewer_helper.rb @@ -73,7 +73,7 @@ links = (article.native_translation.translations + [article.native_translation]).map do |translation| { article.environment.locales[translation.language] => { :href => url_for(translation.url) } } end content_tag(:div, link_to(_('Translations'), '#', - :onmouseover => "toggleSubmenu(this, '#{_('Translations')}', #{CGI::escape_html(links.to_json)}); return false", + :onmouseover => "toggleSubmenu(this, '#{_('Translations')}', #{links.to_json}); return false", :class => 'article-translations-menu simplemenu-trigger up'), :class => 'article-translations') end diff --git a/app/helpers/dates_helper.rb b/app/helpers/dates_helper.rb index 13dea3b9f535e35c2a6e821413d68987ed49b405..536e936d9ba8af638901c3348535686c78eefdfc 100644 --- a/app/helpers/dates_helper.rb +++ b/app/helpers/dates_helper.rb @@ -14,8 +14,11 @@ end end # formats a date for displaying. - def show_date(date, use_numbers = false, year = true, left_time = false) - if date && use_numbers + def show_date(date, use_numbers = false, year = true, left_time = false, abbreviated = false) + if date && abbreviated + date_format = year ? _('%{month_name} %{year}') : _('%{month_name} %{day}') + date_format % { :day => date.day, :month_name => month_name(date.month, true), :year => date.year } + elsif date && use_numbers date_format = year ? _('%{month}/%{day}/%{year}') : _('%{month}/%{day}') date_format % { :day => date.day, :month => date.month, :year => date.year } elsif date && left_time diff --git a/app/helpers/forms_helper.rb b/app/helpers/forms_helper.rb index 8f0772257aa9bfad166f61f1f6ed12bbaf364e45..8629319d65562b2c39d8285ccaf9905e3c8d632e 100644 --- a/app/helpers/forms_helper.rb +++ b/app/helpers/forms_helper.rb @@ -250,8 +250,9 @@ def date_range_field(from_name, to_name, from_value, to_value, datepicker_options = {}, html_options = {}) from_id = html_options[:from_id] || 'datepicker-from-date' to_id = html_options[:to_id] || 'datepicker-to-date' - return (_('From') +' '+ date_field(from_name, from_value, datepicker_options, html_options.merge({:id => from_id})) + - ' ' + _('until') +' '+ date_field(to_name, to_value, datepicker_options, html_options.merge({:id => to_id}))).html_safe + from = content_tag('label', _('From:').html_safe + date_field(from_name, from_value, datepicker_options, html_options.merge({:id => from_id}))) + to = content_tag('label', _('Until:').html_safe + date_field(to_name, to_value, datepicker_options, html_options.merge({:id => to_id}))) + return from + to end def select_folder(label_text, field_id, collection, default_value=nil, html_options = {}, js_options = {}) diff --git a/app/helpers/layout_helper.rb b/app/helpers/layout_helper.rb index e362aadc91fbda74a56b21a0bd1289645372da67..7b6430a7725edcae297047ab81f1d0ae225606c0 100644 --- a/app/helpers/layout_helper.rb +++ b/app/helpers/layout_helper.rb @@ -3,14 +3,13 @@ protected def body_classes - # Identify the current controller and action for the CSS: [ - (logged_in? ? 'logged-in' : nil), + (logged_in? ? " logged-in" : nil), "#{" responsive" if theme_option :responsive}", "controller-#{controller.controller_name}", "action-#{controller.controller_name}-#{controller.action_name}", - "template-#{@layout_template || if profile.blank? then 'default' else profile.layout_template end}", - !profile.nil? && profile.is_on_homepage?(request.path,@page) ? 'profile-homepage' : nil, + "template-#{@layout_template || layout_template}", + (!profile.nil? && profile.is_on_homepage?(request.path,@page) ? "profile-homepage" : nil), profile.present? ? profile.kinds_style_classes : nil, ].compact.join(' ') end @@ -54,7 +53,7 @@ def noosfero_stylesheets plugins_stylesheets = @plugins.select(&:stylesheet?).map { |plugin| plugin.class.public_path('style.css', true) } - global_css_pub = "/designs/themes/#{environment.theme}/global.css" + global_css_pub = "/designs/themes/#{session[:theme] || environment.theme}/global.css" global_css_at_fs = Rails.root.join 'public' + global_css_pub output = [] diff --git a/app/helpers/memberships_helper.rb b/app/helpers/memberships_helper.rb index 3777871b945a0e7dec07d074c38e3a429e1f371e..029f4ce450b0b33a22ca44cab4be8e6d59cb6e31 100644 --- a/app/helpers/memberships_helper.rb +++ b/app/helpers/memberships_helper.rb @@ -5,6 +5,8 @@ url = options[:logged] ? profile.join_url : profile.join_not_logged_url if show_confirmation_modal? profile modal_button :add, _('Join this community'), url, class: 'join-community' + elsif !options[:logged] + modal_button :add, _('Join this community'), url, class: 'join-community' else button :add, _('Join this community'), url, class: 'join-community' end diff --git a/app/helpers/profile_image_helper.rb b/app/helpers/profile_image_helper.rb index d9b46a1651309cfc7fd68b9944bad131ccf5222f..2335775af734ccff0018f8edd0db097c823b7dcc 100644 --- a/app/helpers/profile_image_helper.rb +++ b/app/helpers/profile_image_helper.rb @@ -37,7 +37,7 @@ else '/images/icons-app/enterprise-'+ size.to_s() +'.png' end else - pixels = Image.attachment_options[:thumbnails][size].split('x').first + pixels = Image.attachment_options[:thumbnails][size.to_sym].split('x').first gravatar_profile_image_url( profile.email, :size => pixels, diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 32b3590b4560332558318a5439a49b173af7d752..09bb5dd04d902fa9f98163c62074aaef806c9ead 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -11,6 +11,7 @@ :order => { 'more_popular' => _('More popular'), 'more_active' => _('More active'), 'more_recent' => _('More recent'), + 'more_relevant' => _('More relevant'), 'more_comments' => _('More comments') }, :display => { @@ -38,7 +39,7 @@ # FIXME remove it after search_controler refactored include EventsHelper def multiple_search?(searches=nil) - ['index', 'category_index'].include?(params[:action]) || (searches && searches.size > 1) + ['index', 'category_index', 'tag'].include?(params[:action]) || (searches && searches.size > 1) end def map_search?(searches=nil) @@ -49,9 +50,10 @@ def asset_class(asset) asset.to_s.singularize.camelize.constantize end - def search_page_title(title, category = nil) + def search_page_title(title, options = {}) title = "<h1>" + title - title += ' - <small>' + category.name + '</small>' if category + title += ' - <small>' + options[:category].name + '</small>' if options[:category] + title += ' - <small>' + _('Tagged with') + ' ' + options[:tag] + '</small>' if options[:tag] title += "</h1>" title.html_safe end @@ -131,7 +133,7 @@ end end def filters(asset) - return if !asset + return if !asset || asset == :tag klass = asset_class(asset) content_tag('div', safe_join(klass::SEARCH_FILTERS.map do |name, options| default = klass.respond_to?("default_search_#{name}") ? klass.send("default_search_#{name}".to_s) : nil @@ -155,7 +157,7 @@ :id => 'assets-menu') end def asset_link(asset) - link_to(@enabled_searches[asset], "/search/#{asset}") + link_to(@enabled_searches[asset], {:controller => 'search', :action => asset}, 'data-tag' => @tag, 'data-category_path' => @category.try(:path)) end def assets_submenu(asset) diff --git a/app/helpers/token_helper.rb b/app/helpers/token_helper.rb index 008da6bcfe267cf1637841df8f6783f1a5ee17d5..6781b5fb011d91af8a12342890d7d4804ffb912d 100644 --- a/app/helpers/token_helper.rb +++ b/app/helpers/token_helper.rb @@ -9,7 +9,7 @@ options[:min_chars] ||= 2 options[:hint_text] ||= _("Type in a search term") options[:no_results_text] ||= _("No results") options[:searching_text] ||= _("Searching...") - options[:placeholder] ||= 'null' + options[:placeholder] ||= '' options[:search_delay] ||= 1000 options[:prevent_duplicates] ||= true options[:backspace_delete_item] ||= false diff --git a/app/models/article.rb b/app/models/article.rb index a2339a5242ed6bc173e2a1cc85ae4319f3012515..5d2f57e5826e798a932176a5c61438d69153889d 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -8,6 +8,7 @@ RAW_HTML = 'raw_html' end include SanitizeHelper + include SanitizeTags attr_accessible :name, :body, :abstract, :profile, :tag_list, :parent, :allow_members_to_edit, :translation_of_id, :language, @@ -35,7 +36,7 @@ :filename => {:label => _('Filename'), :weight => 1}, } SEARCH_FILTERS = { - :order => %w[more_recent more_popular more_comments], + :order => %w[more_recent more_popular more_comments more_relevant], :display => %w[full] } @@ -67,10 +68,6 @@ end track_actions :create_article, :after_create, :keep_params => [:name, :url, :lead, :first_image], :if => Proc.new { |a| a.notifiable? } - # xss_terminate plugin can't sanitize array fields - # sanitize_tag_list is used with SanitizeHelper - before_save :sanitize_tag_list - before_create do |article| if article.author article.author_name = article.author.name @@ -103,6 +100,9 @@ has_many :categories_including_virtual, :through => :article_categorizations_including_virtual, :source => :category extend ActsAsHavingSettings::ClassMethods acts_as_having_settings field: :setting + + store_accessor :metadata + include MetadataScopes settings_items :display_hits, :type => :boolean, :default => true settings_items :author_name, :type => :string, :default => "" @@ -551,9 +551,9 @@ scope :no_feeds, -> { where "type != 'RssFeed'" } scope :latest, -> { order "updated_at DESC" } - scope :more_popular, -> { order 'hits DESC' } - scope :more_comments, -> { order "comments_count DESC" } - scope :more_recent, -> { order "created_at DESC" } + scope :more_popular, -> { order 'articles.hits DESC' } + scope :more_comments, -> { order "articles.comments_count DESC" } + scope :more_recent, -> { order "articles.created_at DESC" } scope :display_filter, lambda {|user, profile| return published if (user.nil? && profile && profile.public?) @@ -761,7 +761,9 @@ if version_number then self.versions.order('version').offset(version_number - 1).first else self.versions.earliest end end def author_by_version(version_number = nil) - if version_number then profile.environment.people.where(id: get_version(version_number).author_id).first else author end + return author unless version_number + author_id = get_version(version_number).last_changed_by_id + profile.environment.people.where(id: author_id).first end def author_name(version_number = nil) @@ -914,15 +916,6 @@ self.editor == editor end private - - def sanitize_tag_list - sanitizer = HTML::FullSanitizer.new - self.tag_list.map!{|i| strip_tag_name sanitizer.sanitize(i) } - end - - def strip_tag_name(tag_name) - tag_name.gsub(/[<>]/, '') - end def parent_archived? if self.parent_id_changed? && self.parent && self.parent.archived? diff --git a/app/models/block.rb b/app/models/block.rb index f7fe74b169c3822cc899ca6d2551cfacf9ed7d3f..306c9f45b55fbdf108ea08883992e9d648bc58bc 100644 --- a/app/models/block.rb +++ b/app/models/block.rb @@ -1,8 +1,8 @@ class Block < ApplicationRecord attr_accessible :title, :subtitle, :display, :limit, :box_id, :posts_per_page, - :visualization_format, :language, :display_user, - :box, :edit_modes, :move_modes, :mirror, :visualization, :images_builder + :visualization_format, :language, :display_user, :position, + :box, :edit_modes, :move_modes, :mirror, :visualization, :images_builder, :api_content include ActionView::Helpers::TagHelper @@ -22,6 +22,9 @@ extend ActsAsHavingSettings::ClassMethods acts_as_having_settings settings_items :visualization, :type => Hash, :default => {} + + store_accessor :metadata + include MetadataScopes scope :enabled, -> { where :enabled => true } @@ -307,6 +310,9 @@ end def api_content(options = {}) nil + end + + def api_content=(values = {}) end def display_api_content_by_default? diff --git a/app/models/box.rb b/app/models/box.rb index 45c075e8f69e6e7940fa78ce715991bf537a85d1..62c8f99eb06a28ddb228d42abda990f520a626ed 100644 --- a/app/models/box.rb +++ b/app/models/box.rb @@ -4,8 +4,9 @@ acts_as_list scope: -> box { where owner_id: box.owner_id, owner_type: box.owner_type } belongs_to :owner, :polymorphic => true has_many :blocks, -> { order 'position' }, dependent: :destroy + accepts_nested_attributes_for :blocks, allow_destroy: true - attr_accessible :owner + attr_accessible :owner, :blocks_attributes include Noosfero::Plugin::HotSpot @@ -41,7 +42,8 @@ MyNetworkBlock, ProfileImageBlock, RawHTMLBlock, RecentDocumentsBlock, - TagsBlock, + TagsCloudBlock, + InterestTagsBlock, MenuBlock] end @@ -66,15 +68,23 @@ ProfileSearchBlock, RawHTMLBlock, RecentDocumentsBlock, SlideshowBlock, - TagsBlock, + TagsCloudBlock, + InterestTagsBlock, MenuBlock ] end + def blocks_attributes=(attributes) + attributes.select { |b| b[:id].nil? }.each do |b| + block = b.delete(:type).constantize.new(b) + self.blocks << block + end + assign_nested_attributes_for_collection_association(:blocks, attributes.reject { |b| b[:id].nil? }.map { |b| b.except(:type) }) + end + private def to_css_selector(blocks_classes) blocks_classes.map{ |block_class| ".#{block_class.name.to_css_class}" }.join(',') end - end diff --git a/app/models/community.rb b/app/models/community.rb index 7cba15eef2dae26c4e05e024aa291a5472d6fb20..943cab73913695a3a823788c9a2e25038168467e 100644 --- a/app/models/community.rb +++ b/app/models/community.rb @@ -1,7 +1,8 @@ class Community < Organization - attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, :resource_type - attr_accessible :address_reference, :district, :tag_list, :language, :description + attr_accessible :accessor_id, :accessor_type, :role_id, :resource_id, + :resource_type, :address_reference, :district, :language, :description + attr_accessible :requires_email, :address_line2 after_destroy :check_invite_member_for_destroy @@ -113,7 +114,7 @@ @boxes_limit = 2 self.layout_template = 'rightbar' [ [MenuBlock.new, MainBlock.new], - [CommunitiesBlock.new, TagsBlock.new] + [CommunitiesBlock.new, TagsCloudBlock.new] ] end diff --git a/app/models/concerns/acts_as_having_boxes.rb b/app/models/concerns/acts_as_having_boxes.rb index 687a1a44b460224c241eea09a3a721ca53b58a94..5e79e5a01e6b6449f30594f18cfc723177303ecf 100644 --- a/app/models/concerns/acts_as_having_boxes.rb +++ b/app/models/concerns/acts_as_having_boxes.rb @@ -3,6 +3,7 @@ module ClassMethods def acts_as_having_boxes has_many :boxes, -> { order :position }, as: :owner, dependent: :destroy + accepts_nested_attributes_for :boxes self.send(:include, ActsAsHavingBoxes) end end @@ -34,4 +35,3 @@ @boxes_limit ||= LayoutTemplate.find(layout_template).number_of_boxes || 3 end end - diff --git a/app/models/concerns/metadata_scopes.rb b/app/models/concerns/metadata_scopes.rb new file mode 100644 index 0000000000000000000000000000000000000000..f4855f9a087334ab107a31020626fe882d201338 --- /dev/null +++ b/app/models/concerns/metadata_scopes.rb @@ -0,0 +1,13 @@ +module MetadataScopes + extend ActiveSupport::Concern + + included do + scope :with_metadata, -> metadata { + where metadata.map{ |k, v| "metadata->>'#{k}' = '#{v}'"}.join(' AND ') + } + + scope :has_metadata, -> key { + where "metadata ? '#{key}'" + } + end +end diff --git a/app/models/concerns/sanitize_tags.rb b/app/models/concerns/sanitize_tags.rb new file mode 100644 index 0000000000000000000000000000000000000000..808ff34b220c2cfae20fd702f03e2e04fc4d1bed --- /dev/null +++ b/app/models/concerns/sanitize_tags.rb @@ -0,0 +1,20 @@ +module SanitizeTags + extend ActiveSupport::Concern + + included do + # xss_terminate plugin can't sanitize array fields + # sanitize_tag_list is used with SanitizeHelper + before_save :sanitize_tag_list + end + + private + + def sanitize_tag_list + sanitizer = HTML::FullSanitizer.new + self.tag_list.map!{|i| strip_tag_name sanitizer.sanitize(i) } + end + + def strip_tag_name(tag_name) + tag_name.gsub(/[<>]/, '') + end +end diff --git a/app/models/concerns/search_tags.rb b/app/models/concerns/search_tags.rb new file mode 100644 index 0000000000000000000000000000000000000000..6b9b8690ef0ac7612bd20c53232222883dce69b5 --- /dev/null +++ b/app/models/concerns/search_tags.rb @@ -0,0 +1,9 @@ +module SearchTags + extend ActiveSupport::Concern + + def search_tags + arg = params[:term].downcase + result = Tag.where('name ILIKE ?', "%#{arg}%").limit(10) + render :text => prepare_to_token_input_by_label(result).to_json, :content_type => 'application/json' + end +end diff --git a/app/models/concerns/time_scopes.rb b/app/models/concerns/time_scopes.rb index a3b563540d204cfc9931bf589338a97f7997b6f2..be6a65956d84ec6ff11c4ef9d221bc8f0f7ce808 100644 --- a/app/models/concerns/time_scopes.rb +++ b/app/models/concerns/time_scopes.rb @@ -1,3 +1,10 @@ +# This module provides the following scopes: +# * older_than(:created_at) +# * younger_than(:created_at) +# * created_at(:start_date, :end_date) +# * updated_at(:start_date, :end_date) +# * published_at(:start_date, :end_date) + module TimeScopes def self.included(recipient) recipient.extend(ClassMethods) @@ -5,15 +12,42 @@ end module ClassMethods def self.extended (base) - if base.respond_to?(:scope) && base.attribute_names.include?('created_at') - base.class_eval do - scope :younger_than, lambda { |created_at| - where "#{table_name}.created_at > ?", created_at - } + if base.respond_to?(:scope) + if base.attribute_names.include?('created_at') + base.class_eval do + scope :younger_than, lambda { |created_at| + where "#{table_name}.created_at > ?", created_at + } - scope :older_than, lambda { |created_at| - where "#{table_name}.created_at < ?", created_at - } + scope :older_than, lambda { |created_at| + where "#{table_name}.created_at < ?", created_at + } + end + end + + attributes = %w[updated_at created_at published_at] + attributes.each do |attribute| + if base.attribute_names.include?(attribute) + base.class_eval do + scope attribute, -> start_date, end_date { + if start_date.present? + start_date = DateTime.parse(start_date) unless start_date.kind_of?(DateTime) + start_term = "#{table_name}.#{attribute} > ?" + else + start_date = nil + end + + if end_date.present? + end_date = DateTime.parse(end_date) unless end_date.kind_of?(DateTime) + end_term = "#{table_name}.#{attribute} < ?" + else + end_date = nil + end + + where [start_term, end_term].compact.join(' AND '), *[start_date, end_date].compact + } + end + end end end end diff --git a/app/models/create_community.rb b/app/models/create_community.rb index e8ec2097422f9c6389da54adf1ad52d1e6581bff..2845ea36ed71607861cd506dfbcfd2167bb1878c 100644 --- a/app/models/create_community.rb +++ b/app/models/create_community.rb @@ -15,7 +15,8 @@ extend ActsAsHavingImage::ClassMethods acts_as_having_image - DATA_FIELDS = Community.fields + ['name', 'closed', 'description'] + DATA_FIELDS = Community.fields + %w[ name closed description address + zip_code city state country district lat lng ] DATA_FIELDS.each do |field| settings_items field.to_sym attr_accessible field.to_sym diff --git a/app/models/create_enterprise.rb b/app/models/create_enterprise.rb index f4b08677493c7369065207261a0cb54e8b995c1f..0c6eefd27895e7282983880c6ea6a8d161748e79 100644 --- a/app/models/create_enterprise.rb +++ b/app/models/create_enterprise.rb @@ -11,7 +11,8 @@ N_('Legal form') N_('Economic activity') N_('Management information') - DATA_FIELDS = Enterprise.fields + %w[name identifier region_id] + DATA_FIELDS = Enterprise.fields + %w[ name identifier region_id address + zip_code city state country district lat lng ] - [ 'location' ] DATA_FIELDS.each do |field| settings_items field.to_sym end diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index 17c46e7a15f5d74425cafe1ccf6a1fb5b4e5abeb..bd1cd5f2bd92788e11b9bb7be0c12e09fec6879a 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -9,6 +9,10 @@ validates_presence_of :name, :format, :customized_type, :environment validate :related_to_other? validate :unique? + + before_validation do |custom_field| + custom_field.signup = true if custom_field.required + end def unique? if environment.custom_fields.any?{|cf| cf.name==name && cf.environment == environment && cf.customized_type==customized_type && new_record?} diff --git a/app/models/custom_field_value.rb b/app/models/custom_field_value.rb index a42de57d5c326bb630697c47dad5f054b817468e..323762544ebb082397a728ff6567be8de6b24e4b 100644 --- a/app/models/custom_field_value.rb +++ b/app/models/custom_field_value.rb @@ -5,6 +5,10 @@ belongs_to :customized, :polymorphic => true attr_accessible :value, :public, :customized, :custom_field, :customized_type validate :can_save? + scope :only_public, -> { where(:public => true) } + scope :not_public, -> { where(:public => false) } + scope :by_field, lambda { |field| self.joins(:custom_field).where("custom_fields.name = ?", field) } + def can_save? if value.blank? && custom_field.required errors.add(custom_field.name, _("can't be blank")) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index cec25ef3b7185288cb79674828769fdf7c8c889c..bd5f8c6ab40157fdb5988f743379f4126ec52e16 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -1,7 +1,8 @@ class Enterprise < Organization - attr_accessible :business_name, :address_reference, :district, :tag_list, - :organization_website, :historic_and_current_context, :activities_short_description + attr_accessible :business_name, :address_reference, :district, + :organization_website, :historic_and_current_context, + :activities_short_description SEARCH_FILTERS = { :order => %w[more_recent more_popular more_active], @@ -176,4 +177,7 @@ def more_recent_label '' end + def available_blocks(person) + super(person) + [DisabledEnterpriseMessageBlock, HighlightsBlock, FansBlock] + end end diff --git a/app/models/environment.rb b/app/models/environment.rb index fe1f277ae8ae6518a56f79b8a145c0a7415d1a34..5b355899f5e069f696fc3f2f6b599f6306fafe16 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -15,7 +15,7 @@ :signup_welcome_screen_body, :members_whitelist_enabled, :members_whitelist, :highlighted_news_amount, :portal_news_amount, :date_format, :signup_intro, :enable_feed_proxy, :http_feed_proxy, :https_feed_proxy, - :disable_feed_ssl, :layout_template + :disable_feed_ssl, :layout_template, :boxes_attributes has_many :users @@ -26,10 +26,13 @@ end has_many :tasks, :dependent => :destroy, :as => 'target' has_many :search_terms, :as => :context + has_many :email_templates, :foreign_key => :owner_id has_many :custom_fields, :dependent => :destroy - has_many :email_templates, :foreign_key => :owner_id + has_many :person_custom_fields, -> { where(customized_type: 'Person')}, class_name: 'CustomField' + has_many :community_custom_fields, -> { where(customized_type: 'Community')}, class_name: 'CustomField' + has_many :enterprise_custom_fields, -> { where(customized_type: 'Enterprise')}, class_name: 'CustomField' - IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/ + IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/ unless const_defined?(:IDENTIFY_SCRIPTS) validates_inclusion_of :date_format, :in => [ 'numbers_with_year', 'numbers', @@ -506,6 +509,15 @@ self.settings[:organization_approval_method] = actual_value end + def all_custom_person_fields + fields = self.settings[:custom_person_fields].nil? ? {} : self.settings[:custom_person_fields] + self.person_custom_fields.map do |cf| + fields[cf.name] = {'active' => cf.active.to_s, 'required' => cf.required.to_s, 'signup' => cf.signup.to_s } + end + + fields + end + def custom_person_fields self.settings[:custom_person_fields].nil? ? {} : self.settings[:custom_person_fields] end @@ -566,6 +578,15 @@ message_for_member_invitation end end + def all_custom_enterprise_fields + fields = self.settings[:custom_enterprise_fields].nil? ? {} : self.settings[:custom_enterprise_fields] + self.enterprise_custom_fields.map do |cf| + fields[cf.name] = {'active' => cf.active.to_s, 'required' => cf.required.to_s, 'signup' => cf.signup.to_s } + end + + fields + end + def custom_enterprise_fields self.settings[:custom_enterprise_fields].nil? ? {} : self.settings[:custom_enterprise_fields] end @@ -610,9 +631,19 @@ end signup_fields end + def all_custom_community_fields + fields = self.settings[:custom_community_fields].nil? ? {} : self.settings[:custom_community_fields] + self.community_custom_fields.map do |cf| + fields[cf.name] = {'active' => cf.active.to_s, 'required' => cf.required.to_s, 'signup' => cf.signup.to_s } + end + + fields + end + def custom_community_fields self.settings[:custom_community_fields].nil? ? {} : self.settings[:custom_community_fields] end + def custom_community_fields=(values) self.settings[:custom_community_fields] = values.delete_if { |key, value| ! Community.fields.include?(key) } self.settings[:custom_community_fields].each_pair do |key, value| @@ -761,11 +792,25 @@ end has_many :events, :through => :profiles, :source => :articles, :class_name => 'Event' - has_many :tags, :through => :articles + has_many :article_tags, :through => :articles, :source => :tags + has_many :profile_tags, :through => :profiles, :source => :tags + + include ScopeTool + scope :tags, -> environment {ScopeTool.union(environment.article_tags, environment.profile_tags)} - def tag_counts - articles.tag_counts.inject({}) do |memo,tag| + def tags + self.class.tags(self) + end + + + def environment_tags + results = articles.tag_counts.inject({}) do |memo,tag| memo[tag.name] = tag.count + memo + end + + profiles.tag_counts.inject(results) do |memo,tag| + memo[tag.name].present? ? memo[tag.name] += tag.count : memo[tag.name] = tag.count memo end end @@ -1037,6 +1082,30 @@ end def permissions_for(person) person.role_assignments.where(resource: self).map {|ra| ra.role.permissions}.flatten.uniq + end + + def available_blocks(person) + core_blocks = [ ArticleBlock, LoginBlock, RecentDocumentsBlock, EnterprisesBlock, + CommunitiesBlock, LinkListBlock, FeedReaderBlock, SlideshowBlock, + HighlightsBlock, CategoriesBlock, RawHTMLBlock, TagsCloudBlock ] + core_blocks + plugins.dispatch(:extra_blocks, type: self.class) + end + + include Noosfero::Plugin::HotSpot + def environment + self + end + + def reserved_identifiers + plugins.dispatch(:reserved_identifiers).inject([]) do |result, identifier| + result << identifier.to_s + end + end + + def is_identifier_available?(identifier, profile_id = nil) + profiles = environment.profiles.where(:identifier => identifier) + profiles = profiles.where(['id != ?', profile_id]) if profile_id.present? + !reserved_identifiers.include?(identifier) && !profiles.exists? end private diff --git a/app/models/event.rb b/app/models/event.rb index 597b8e3549b7d3db0532469e50ddc13ec6059790..cfe9e2bacfd5c2b95f8c230eeb7b180347f8f724 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -45,8 +45,9 @@ } scope :next_events_from_month, -> date { date_temp = date.strftime("%Y-%m-%d") + final_day = date.at_end_of_month order('start_date ASC') - .where("start_date >= ?","#{date_temp}") + .where("start_date >= ? AND start_date <= ?", "#{date_temp}", "#{final_day}") } scope :by_month, -> date { diff --git a/app/models/google_maps.rb b/app/models/google_maps.rb index d1268c7f686311e0ec50927b0386d8b48b04205c..d0d493155e7d0949ef693801f5eccc9deadeb576 100644 --- a/app/models/google_maps.rb +++ b/app/models/google_maps.rb @@ -4,4 +4,8 @@ def self.initial_zoom NOOSFERO_CONF['googlemaps_initial_zoom'] || 4 end + def self.api_key + NOOSFERO_CONF['googlemaps_api_key'] + end + end diff --git a/app/models/interest_tags_block.rb b/app/models/interest_tags_block.rb new file mode 100644 index 0000000000000000000000000000000000000000..db23d1969664c78f2d55aba3d7d593732d77381b --- /dev/null +++ b/app/models/interest_tags_block.rb @@ -0,0 +1,29 @@ +class InterestTagsBlock < Block + def view_title + self.default_title + end + + def tags + owner.tags + end + + def extra_option + {} + end + + def self.description + _('Tags of interest') + end + + def help + _('Contents that this person is interested in') + end + + def default_title + _('Interest Tags') + end + + def self.expire_on + { profile: [:profile] } + end +end diff --git a/app/models/menu_block.rb b/app/models/menu_block.rb index 1280188d80a665efa65cf161984d6eee329decce..6b3bc2fc377ff29ab588468df05d6c96e8d39e9a 100644 --- a/app/models/menu_block.rb +++ b/app/models/menu_block.rb @@ -1,6 +1,10 @@ class MenuBlock < Block include SanitizeHelper + + attr_accessible :enabled_links, :api_content + settings_items :enabled_links, type: Array, :default => [] + def self.description _('Menu Block') end @@ -13,20 +17,30 @@ def self.pretty_name _('Menu Block') end - def enabled_links(user) + def available_links links = [] - links << {title: _('Activities'), controller: 'profile', action: 'activities'} if display_activities?(user) - links << {title: _('About'), controller: 'profile', action: 'about'} if display_about?(user) - links << {title: _('Communities'), controller: 'memberships', action: 'index'} if display_communities?(user) - links << {title: _('People'), controller: 'friends', action: 'index'} if display_friends?(user) - links << {title: _('People'), controller: 'profile_members', action: 'index'} if display_members?(user) - links << {title: _('Control Panel')}.merge(owner.admin_url) if display_control_panel?(user) + links << {title: _('Activities'), controller: 'profile', action: 'activities', condition: -> (user) { display_activities?(user) } } + links << {title: _('About'), controller: 'profile', action: 'about', condition: -> (user) { display_about?(user) } } + links << {title: _('Communities'), controller: 'memberships', action: 'index', condition: -> (user) { display_communities?(user) } } + links << {title: _('People'), controller: 'friends', action: 'index', condition: -> (user) { display_friends?(user) } } + links << {title: _('People'), controller: 'profile_members', action: 'index', condition: -> (user) { display_members?(user) } } + links << {title: _('Control Panel'), condition: -> (user) { display_control_panel?(user) } }.merge(owner.admin_url) links end + def enabled_links_for(user) + filter_links user, enabled_links.empty? ? available_links : enabled_links + end + def api_content(options = {}) - links = self.enabled_links(options[:current_person]) - links + { + enabled_items: enabled_links_for(options[:current_person]), + available_items: filter_links(options[:current_person], available_links) + } + end + + def api_content=(values = {}) + settings[:enabled_links] = values[:enabled_items] end def display_api_content_by_default? @@ -35,15 +49,19 @@ end protected + def filter_links(user, links) + links.select { |link| permission_control(link, user) } + end + def display_control_panel?(user) user && user.has_permission?('edit_profile', owner) end - + def display_activities?(user) AccessLevels.can_access?(access_level, user, owner) end - def access_level + def access_level owner.person? ? AccessLevels::LEVELS[:users] : AccessLevels::LEVELS[:visitors] end @@ -60,7 +78,17 @@ owner.person? && user && user.has_permission?(:manage_friends, owner) end def display_members?(user) - owner.community? && user && user.has_permission?(:manage_memberships, owner) + owner.community? + end + + def display_article?(user) + true + end + + def permission_control(link, user) + return true if !link[:controller] || !link[:action] + available_link = available_links.find { |l| l[:controller] == link[:controller] && l[:action] == link[:action] } + return available_link[:condition].call(user) end end diff --git a/app/models/organization.rb b/app/models/organization.rb index 5d8aead22782832c33e74eecb951df3c25d830fa..8e1ed33167d6ca9fe18df0bdea305bc06c9ce539 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -38,7 +38,7 @@ scope :visible_for_person, lambda { |person| listed_for_person(person).where( [' ( ( role_assignments.accessor_type = ? AND role_assignments.accessor_id = ? ) OR - ( profiles.enabled = ? AND profiles.public_profile = ? ) )', + ( profiles.enabled = ? AND profiles.public_profile = ? ) )', Profile.name, person.id, true, true] ) } @@ -66,7 +66,7 @@ has_many :mailings, :class_name => 'OrganizationMailing', :foreign_key => :source_id, :as => 'source' has_many :custom_roles, :class_name => 'Role', :foreign_key => :profile_id - scope :more_popular, -> { order 'members_count DESC' } + scope :more_popular, -> { order 'profiles.members_count DESC' } validate :presence_of_required_fieds, :unless => :is_template @@ -119,17 +119,11 @@ contact_phone legal_form economic_activity management_information - address - zip_code - city - state - country - tag_list template_id - district address_line2 address_reference profile_kinds + location ] def self.fields diff --git a/app/models/person.rb b/app/models/person.rb index 729f4cde326aae3691f01049a05ed7d4f2289216..97c5c62bb82127cc958ed353acd7313614ee6b80 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -1,7 +1,7 @@ # A person is the profile of an user holding all relationships with the rest of the system class Person < Profile - attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization_website, :following_articles, :editor + attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, :address_line2, :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization_website, :following_articles, :editor SEARCH_FILTERS = { :order => %w[more_recent], @@ -15,6 +15,22 @@ end N_('person') + def self.human_attribute_name_with_customization(attrib, options={}) + case attrib.to_sym + when :lat + _('Latitude') + when :lng + _('Longitude') + when :address + _('Address (street and number)') + else + _(self.human_attribute_name_without_customization(attrib)) + end + end + class << self + alias_method_chain :human_attribute_name, :customization + end + acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)} acts_as_accessor @@ -126,7 +142,7 @@ }, through: :suggested_profiles, source: :suggestion has_and_belongs_to_many :marked_scraps, :join_table => :private_scraps, :class_name => 'Scrap' - scope :more_popular, -> { order 'friends_count DESC' } + scope :more_popular, -> { order 'profiles.friends_count DESC' } scope :abusers, -> { joins(:abuse_complaints).where('tasks.status = 3').distinct.select('profiles.*') @@ -249,14 +265,6 @@ nickname sex birth_date nationality - country - state - city - district - zip_code - address - address_line2 - address_reference cell_phone comercial_phone personal_website @@ -271,6 +279,7 @@ organization organization_website contact_phone contact_information + location ] validates_multiparameter_assignments @@ -280,12 +289,27 @@ FIELDS end validate :presence_of_required_fields, :unless => :is_template + + # Special cases for presence_of_required_fields. You can set: + # - cond: to be executed rather than checking if the field is blank + # - unless: an exception for when the field is not present + # - to_fields: map the errors to these fields rather than `field` + REQUIRED_FIELDS_EXCEPTIONS = { + custom_area_of_study: { unless: Proc.new{|p| p.area_of_study != 'Others' } }, + custom_formation: { unless: Proc.new{|p| p.formation != 'Others' } }, + location: { cond: Proc.new{|p| p.lat.nil? || p.lng.nil? }, to_fields: [:lat, :lng] } + } def presence_of_required_fields self.required_fields.each do |field| - if self.send(field).blank? - unless (field == 'custom_area_of_study' && self.area_of_study != 'Others') || (field == 'custom_formation' && self.formation != 'Others') - self.errors.add_on_blank(field) + opts = REQUIRED_FIELDS_EXCEPTIONS[field.to_sym] || {} + if (opts[:cond] ? opts[:cond].call(self) : self.send(field).blank?) + unless opts[:unless].try(:call, self) + fields = opts[:to_fields] || field + fields = fields.kind_of?(Array) ? fields : [fields] + fields.each do |to_field| + self.errors.add_on_blank(to_field) + end end end end @@ -399,7 +423,7 @@ @boxes_limit = 2 self.layout_template = 'rightbar' [ [MenuBlock.new, MainBlock.new], - [FriendsBlock.new, CommunitiesBlock.new, TagsBlock.new] + [FriendsBlock.new, CommunitiesBlock.new, TagsCloudBlock.new] ] end @@ -644,6 +668,10 @@ Article::Editor::TEXTILE => _('Textile') } available_editors.merge!({Article::Editor::RAW_HTML => _('Raw HTML')}) if self.is_admin? available_editors + end + + def available_blocks(person) + super(person) + [FavoriteEnterprisesBlock, CommunitiesBlock, EnterprisesBlock] end end diff --git a/app/models/person_notifier.rb b/app/models/person_notifier.rb index cf7fa9d54e1944c1a349f9d1623e11e14a62d2cd..12b8db464d2bd7c5e550d6e4c32151ca4ae16f44 100644 --- a/app/models/person_notifier.rb +++ b/app/models/person_notifier.rb @@ -67,12 +67,8 @@ Person.find(person_id).notifier.notify end def failure(job) - begin - person = Person.find(person_id) - person.notifier.dispatch_notification_mail - rescue - Rails.logger.error "PersonNotifier::NotifyJob: Cannot recover from failure" - end + person = Person.find(person_id) + person.notifier.dispatch_notification_mail end end @@ -80,6 +76,17 @@ class Mailer < ApplicationMailer helper ActionTrackerHelper + helper do + def render_activity(activity) + begin + render activity.verb, activity: activity + rescue => error + Delayed::Worker.logger.warn "PersonNotifier::NotifyJob: Cannot "\ + "render template for #{activity.verb} "\ + "notification: #{error}" + end + end + end def session {:user_theme => nil} diff --git a/app/models/profile.rb b/app/models/profile.rb index 7dcd2d68a644654cc05f5ea7b75842eb74369dc2..cf7b3d05bb20f428e9e331ff719605fa0f51d667 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -12,7 +12,7 @@ :redirect_l10n, :notification_time, :redirection_after_login, :custom_url_redirection, :layout_template, :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification, :allow_followers, :wall_access, - :profile_kinds + :profile_kinds, :tag_list, :boxes_attributes attr_accessor :old_region_id @@ -202,6 +202,9 @@ extend ActsAsHavingSettings::ClassMethods acts_as_having_settings field: :data + store_accessor :metadata + include MetadataScopes + def settings data end @@ -252,8 +255,8 @@ scope :disabled, -> { where 'profiles.enabled <> ?', true } # subclass specific scope :more_popular, -> { } - scope :more_active, -> { order 'activities_count DESC' } - scope :more_recent, -> { order "created_at DESC" } + scope :more_active, -> { order 'profiles.activities_count DESC' } + scope :more_recent, -> { order "profiles.created_at DESC" } scope :followed_by, -> person{ distinct.select('profiles.*'). @@ -464,13 +467,11 @@ end def self.is_available?(identifier, environment, profile_id=nil) return false unless identifier =~ IDENTIFIER_FORMAT && - !RESERVED_IDENTIFIERS.include?(identifier) && + !Profile::RESERVED_IDENTIFIERS.include?(identifier) && (NOOSFERO_CONF['exclude_profile_identifier_pattern'].blank? || identifier !~ /#{NOOSFERO_CONF['exclude_profile_identifier_pattern']}/) return true if environment.nil? - profiles = environment.profiles.where(:identifier => identifier) - profiles = profiles.where(['id != ?', profile_id]) if profile_id.present? - !profiles.exists? + environment.is_identifier_available?(identifier, profile_id) end def self.visible_for_person(person) @@ -577,6 +578,8 @@ end xss_terminate :only => [ :name, :nickname, :address, :contact_phone, :description ], :on => 'validation' xss_terminate :only => [ :custom_footer, :custom_header ], :with => 'white_list' + + include SanitizeTags include WhiteListFilter filter_iframes :custom_header, :custom_footer @@ -753,10 +756,6 @@ articles.tag_counts.inject({}) do |memo,tag| memo[tag.name] = tag.count memo end - end - - def tagged_with(tag) - self.articles.tagged_with(tag) end # Tells whether a specified profile has members or nor. @@ -1160,16 +1159,26 @@ not (!self.public_fields.include? field.to_s and (!user or (user != self and !user.is_a_friend?(self)))) end end - def may_display_location_to? user = nil - LOCATION_FIELDS.each do |field| - return false if !self.may_display_field_to? field, user - end - return true - end - # field => privacy (e.g.: "address" => "public") def fields_privacy + self.data[:fields_privacy] ||= {} + custom_field_privacy = {} + self.custom_field_values.includes(:custom_field).pluck("custom_fields.name", :public).to_h.map do |field, is_public| + custom_field_privacy[field] = 'public' if is_public + end + self.data[:fields_privacy].merge!(custom_field_privacy) + self.data[:fields_privacy] + end + + def custom_field_value(field_name) + value = nil + begin + value = self.send(field_name) + rescue NoMethodError + value = self.custom_field_values.by_field(field_name).pluck(:value).first + end + value end # abstract @@ -1234,5 +1243,14 @@ end def in_circle?(circle, follower) ProfileFollower.with_follower(follower).with_circle(circle).with_profile(self).present? + end + + def available_blocks(person) + blocks = [ ArticleBlock, TagsCloudBlock, InterestTagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock, HighlightsBlock, MenuBlock ] + # block exclusive to profiles that have blog + blocks << BlogArchivesBlock if self.has_blog? + # block exclusive for environment admin + blocks << RawHTMLBlock if person.present? && person.is_admin?(self.environment) + blocks + plugins.dispatch(:extra_blocks, type: self.class) end end diff --git a/app/models/profile_categorization.rb b/app/models/profile_categorization.rb index 1147a9703a3e04863f8acffbab116ae0a334ca69..5be8245d0ea0b8d1f3d34ac5021b1fc06a90048c 100644 --- a/app/models/profile_categorization.rb +++ b/app/models/profile_categorization.rb @@ -2,6 +2,7 @@ class ProfileCategorization < ApplicationRecord self.table_name = :categories_profiles belongs_to :profile belongs_to :category + belongs_to :region, :foreign_key => 'category_id' extend Categorization diff --git a/app/models/scrap.rb b/app/models/scrap.rb index fb1868f3316ecae8b9b6c58a91d0599b540af7e6..38f16ff997be68fdb3f6f307fca7f3c78db2baaf 100644 --- a/app/models/scrap.rb +++ b/app/models/scrap.rb @@ -32,7 +32,7 @@ track_actions :leave_scrap, :after_create, :keep_params => ['sender.name', 'content', 'receiver.name', 'receiver.url'], :if => Proc.new{|s| s.sender != s.receiver && s.sender != s.top_root.receiver}, :custom_target => :action_tracker_target, :custom_user => :sender track_actions :leave_scrap_to_self, :after_create, :keep_params => ['sender.name', 'content'], :if => Proc.new{|s| s.sender == s.receiver}, :custom_user => :sender - track_actions :reply_scrap_on_self, :after_create, :keep_params => ['sender.name', 'content'], :if => Proc.new{|s| s.sender != s.receiver && s.sender == s.top_root.receiver}, :custom_user => :sender + track_actions :reply_scrap_on_self, :after_create, :keep_params => ['sender.name', 'content', 'receiver.name', 'receiver.url'], :if => Proc.new{|s| s.sender != s.receiver && s.sender == s.top_root.receiver}, :custom_user => :sender after_create :send_notification diff --git a/app/models/tags_block.rb b/app/models/tags_block.rb deleted file mode 100644 index 1d1a618cb435cc44609d9d443f1a26e6a6b2faa3..0000000000000000000000000000000000000000 --- a/app/models/tags_block.rb +++ /dev/null @@ -1,39 +0,0 @@ -class TagsBlock < Block - - include TagsHelper - include BlockHelper - include ActionView::Helpers - include Rails.application.routes.url_helpers - - settings_items :limit, :type => :integer, :default => 12 - - def self.description - _('<p>Display a tag cloud with the content produced where the block is applied.</p> <p>The user could limit the number of tags will be displayed.</p>') - end - - def self.short_description - _('Display a tag cloud about current content') - end - - def self.pretty_name - _('Tag Cloud') - end - - def default_title - _('tags') - end - - def help - _("Tags are created when you add some of them one to your contents. <p/> - Try to add some tags to some articles and you'l see your tag cloud growing.") - end - - def timeout - 15.minutes - end - - def self.expire_on - { :profile => [:article], :environment => [:article] } - end - -end diff --git a/app/models/tags_cloud_block.rb b/app/models/tags_cloud_block.rb new file mode 100644 index 0000000000000000000000000000000000000000..ecc5848faec56f746a1f04461e4040c2ac397313 --- /dev/null +++ b/app/models/tags_cloud_block.rb @@ -0,0 +1,39 @@ +class TagsCloudBlock < Block + + include TagsHelper + include BlockHelper + include ActionView::Helpers + include Rails.application.routes.url_helpers + + settings_items :limit, :type => :integer, :default => 12 + + def self.description + _('<p>Display a tag cloud with the content produced where the block is applied.</p> <p>The user could limit the number of tags will be displayed.</p>') + end + + def self.short_description + _('Display a tag cloud about current content') + end + + def self.pretty_name + _('Tag Cloud') + end + + def default_title + _('Tags Cloud') + end + + def help + _("Tags are created when you add some of them one to your contents or mark a profile with them. <p/> + Try to create some tags and you'll see your tag cloud growing.") + end + + def timeout + 15.minutes + end + + def self.expire_on + { :profile => [:article], :environment => [:article] } + end + +end diff --git a/app/models/task.rb b/app/models/task.rb index e7833b1058fd6e5a152adfcefbf48f134147de6b..0a98b09c856fa4da82854778e05e9bce3ca633aa 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -14,6 +14,9 @@ extend ActsAsHavingSettings::ClassMethods acts_as_having_settings field: :data + store_accessor :metadata + include MetadataScopes + module Status # the status of tasks just created ACTIVE = 1 diff --git a/app/models/user.rb b/app/models/user.rb index 16a2fb7c8c98f04559aca2e0d0f104d708eb1373..129eb4977655d2943014d4db9605eb91c685ce06 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,6 +7,9 @@ class User < ApplicationRecord attr_accessible :login, :email, :password, :password_confirmation, :activated_at + store_accessor :metadata + include MetadataScopes + N_('Password') N_('Password confirmation') N_('Terms accepted') diff --git a/app/views/blocks/highlights.html.erb b/app/views/blocks/highlights.html.erb index ab534efb1f8f4b82dfb0977a1c220cd7eefe5eea..07415e6c6f3b646a9d07abf7bcee980d0bc1e9b7 100644 --- a/app/views/blocks/highlights.html.erb +++ b/app/views/blocks/highlights.html.erb @@ -1,12 +1,17 @@ <%= block_title(block.title, block.subtitle) %> <% if !block.featured_images.empty? %> - <div class='highlights-border'> - <div class='highlights-container'> + <div class="highlights-border"> + <div class="highlights-container"> <% block.featured_images.each do |img| %> - <a href="<%= img[:address] %>" <%= 'target="_blank"' if img[:new_window] %> title="<%= img[:title] %>" class="highlights-image-link"> - <%= image_tag [Noosfero.root, img[:image_src]].join, alt: img[:title] %> - <p class="highlights-label"><%= img[:title] %></p> + <% imgsrc = "#{Noosfero.root}#{img[:image_src]}" %> + <a href="<%= img[:address] %>" + target="<%= '_blank' if img[:new_window] %>" + title="<%= img[:title] %>" + class="highlights-image-link"> + <div class="highlights-img2" style="background-image:url(<%=imgsrc%>)"></div> + <div class="highlights-img1" style="background-image:url(<%=imgsrc%>)"></div> + <p class="highlights-label <%= 'empty' if img[:title].blank? %>"><%= img[:title] %></p> </a> <% end %> </div> diff --git a/app/views/blocks/interest_tags.html.erb b/app/views/blocks/interest_tags.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..85a027e9ba752fef017caa287e61c6914ca5b582 --- /dev/null +++ b/app/views/blocks/interest_tags.html.erb @@ -0,0 +1,14 @@ +<%= block_title(block.view_title, block.subtitle) %> + +<div class="interest-tags-block"> + <% unless block.tags.size == 0 %> + <ul> + <% block.tags.each do |tag| %> + <%= link_to(content_tag('li', tag.name), {:controller => 'search', :action => 'tag', :tag => tag.name}) %> + <% end %> + </ul> + <% else %> + <div class="interest-tags-block-none"><%= c_('None') %></div> + <% end %> + <br style="clear:both" /> +</div> diff --git a/app/views/blocks/menu.html.erb b/app/views/blocks/menu.html.erb index a6332de17092f75c1d576c61d13271ac1ad85505..7566dc73b24a885dae899f89fdd2d37931063231 100644 --- a/app/views/blocks/menu.html.erb +++ b/app/views/blocks/menu.html.erb @@ -1,9 +1,11 @@ <%= block_title(block.title, block.subtitle) %> <ul> - <% block.enabled_links(user).each do |link| %> - <li class="<%= current_page?(:controller => link[:controller], :action => link[:action]) ? 'active' : '' %>"> - <%= link_to link[:title], {:controller => link[:controller], :action => link[:action], :profile => block.owner.identifier} %> - </li> + <% block.enabled_links_for(user).each do |link| %> + <% if link[:controller] %> + <li class="<%= current_page?(:controller => link[:controller], :action => link[:action]) ? 'active' : '' %>"> + <%= link_to link[:title], {:controller => link[:controller], :action => link[:action], :profile => block.owner.identifier} %> + </li> + <% end %> <% end %> </ul> diff --git a/app/views/blocks/tags.html.erb b/app/views/blocks/tags.html.erb deleted file mode 100644 index cd639bafd183ffe9b7657f53c444ab6362314293..0000000000000000000000000000000000000000 --- a/app/views/blocks/tags.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -<% extend TagsHelper %> -<%= block_title(block.title, block.subtitle) %> - -<% - is_env = block.owner.class == Environment - tags = is_env ? block.owner.tag_counts : block.owner.article_tags - if block.limit - tags_tmp = tags.sort_by{ |k,v| -v }[0..(block.limit-1)] - tags = {} - tags_tmp.map{ |k,v| tags[k] = v } - end -%> - -<% unless tags.empty? %> - <div class='tag_cloud'> - <% if is_env %> - <%= tag_cloud(tags, :tag, - {:host => block.owner.default_hostname, :controller=>'search', :action => 'tag'}, - :max_size => 16, :min_size => 9) %> - <% else %> - <%= tag_cloud(tags, :id, - block.owner.public_profile_url.merge(:controller => 'profile', :action => 'content_tagged'), - :max_size => 16, :min_size => 9) %> - <% end %> - </div> -<% end %> diff --git a/app/views/blocks/tags_cloud.html.erb b/app/views/blocks/tags_cloud.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..a49a5bd3a7f3cccb2bfaaab09603a317000a8142 --- /dev/null +++ b/app/views/blocks/tags_cloud.html.erb @@ -0,0 +1,26 @@ +<% extend TagsHelper %> +<%= block_title(block.title, block.subtitle) %> + +<% + is_env = block.owner.class == Environment + tags = is_env ? block.owner.environment_tags : block.owner.article_tags + if block.limit + tags_tmp = tags.sort_by{ |k,v| -v }[0..(block.limit-1)] + tags = {} + tags_tmp.map{ |k,v| tags[k] = v } + end +%> + +<% unless tags.empty? %> + <div class='tag_cloud'> + <% if is_env %> + <%= tag_cloud(tags, :tag, + {:host => block.owner.default_hostname, :controller=>'search', :action => 'tag'}, + :max_size => 16, :min_size => 9) %> + <% else %> + <%= tag_cloud(tags, :id, + block.owner.public_profile_url.merge(:controller => 'profile', :action => 'content_tagged'), + :max_size => 16, :min_size => 9) %> + <% end %> + </div> +<% end %> diff --git a/app/views/box_organizer/show_block_type_info.html.erb b/app/views/box_organizer/show_block_type_info.html.erb index b9fc83c9b684abdbc2e33105a627996157946716..9a6e53759d1f0cc6da8428c5cdb45acd905e19ff 100644 --- a/app/views/box_organizer/show_block_type_info.html.erb +++ b/app/views/box_organizer/show_block_type_info.html.erb @@ -14,7 +14,7 @@
<span style="font-size: 14px;"><%= link_to(activity.params['name'], activity.params['url'], :style => "color: #333; font-weight: bold; text-decoration: none;") %></span> <br/> <span title='<%= activity.target.class.short_description %>' class='profile-activity-icon icon-new icon-new<%= activity.target.class.icon_name %>'></span> - <%= image_tag(activity.params['first_image'], :style => 'max-width:100%;') unless activity.params['first_image'].blank? %><%= strip_tags(truncate(activity.params['lead'], :length => 1000, :ommision => '...')).gsub(/(\xC2\xA0|\s)+/, ' ').gsub(/^\s+/, '') unless activity.params['lead'].blank? %> + <%= image_tag(activity.params['first_image'], :style => 'max-width:100%;') unless activity.params['first_image'].blank? %><%= strip_tags(truncate(activity.params['lead'].html_safe, :length => 1000, :ommision => '...', :escape => false)).gsub(/(\xC2\xA0|\s)+/, ' ').gsub(/^\s+/, '') unless activity.params['lead'].blank? %> </p> <p><%= content_tag(:p, link_to(_('See complete forum'), activity.get_url), :class => 'see-forum') if activity.target.is_a?(Forum) %></p> </td> diff --git a/app/views/person_notifier/mailer/content_summary.html.erb b/app/views/person_notifier/mailer/content_summary.html.erb index 04557df4d868267a6000a223f8186a4d9852b92e..6ca7bf9f46de8f767f61922c8c108c453a278bbd 100644 --- a/app/views/person_notifier/mailer/content_summary.html.erb +++ b/app/views/person_notifier/mailer/content_summary.html.erb @@ -25,7 +25,7 @@