Merge branch 'release/0.7.0.0'

This commit is contained in:
Benjamin Neff 2017-08-19 22:34:33 +02:00
commit 7153df5c86
No known key found for this signature in database
GPG key ID: 971464C3F1A90194
605 changed files with 11175 additions and 7110 deletions

23
.gitignore vendored
View file

@ -10,27 +10,15 @@ app/assets/images/custom/
# Configuration files
config/diaspora.yml
config/heroku.yml
config/initializers/secret_token.rb
config/redis.conf
config/deploy_config.yml
config/schedule.rb
.bundle
vendor/bundle/
vendor/cache/
config/database.yml
.rvmrc_custom
.rvmrc.local
config/oidc_key.pem
# Mailing list stuff
config/email_offset
config/mailing_list.csv
# Generated files
log/
public/stylesheets/*.css
public/diaspora
spec/fixtures/*.y*ml
spec/fixtures/*.fixture.*
coverage/
@ -39,6 +27,12 @@ public/404.html
public/422.html
public/500.html
# the db/schema.rb. Although this is contrary to rails best-practises, we
# cannot provide a schema.rb that works for both MySQL and PostgreSQL, so we
# have no choice. Our migrations are maintained, so it should always be
# possible to get back to a "clean" database schema anyway.
db/schema.rb
# Sprites
app/assets/images/branding-*.png
app/assets/images/branding/logos-*.png
@ -53,17 +47,13 @@ doc/
public/uploads/
public/assets/
public/source.tar*
public/.well-known
tmp/**/
tmp/
*.sqlite3
# Temporary files of every sort
.sass-cache/
.DS_Store
.idea
.redcar
.rvmrc
.stgit*
*.swap
*.swo
@ -76,7 +66,6 @@ tmp/
nbproject
patches-*
capybara-*.html
dump.rdb
# Rubinius's JIT
*.rbc

View file

@ -40,7 +40,7 @@ Metrics/BlockLength:
# No space makes the method definition shorter and differentiates
# from a regular assignment.
Style/SpaceAroundEqualsInParameterDefault:
Layout/SpaceAroundEqualsInParameterDefault:
EnforcedStyle: no_space
# Single quotes being faster is hardly measurable and only affects parse time.
@ -55,7 +55,7 @@ Style/SymbolArray:
Enabled: true
# Most readable form.
Style/AlignHash:
Layout/AlignHash:
EnforcedHashRocketStyle: table
EnforcedColonStyle: table
@ -92,7 +92,7 @@ Style/RaiseArgs:
# Indenting the chained dots beneath each other is not supported by this cop,
# see https://github.com/bbatsov/rubocop/issues/1633
Style/MultilineOperationIndentation:
Layout/MultilineOperationIndentation:
Enabled: false
# Fail is an alias of raise. Avoid aliases, it's more cognitive load for no gain.
@ -107,7 +107,7 @@ Style/SignalException:
Lint/HandleExceptions:
Enabled: false
Style/SpaceInsideBlockBraces:
Layout/SpaceInsideBlockBraces:
# The space here provides no real gain in readability while consuming
# horizontal space that could be used for a better parameter name.
# Also {| differentiates better from a hash than { | does.
@ -115,7 +115,7 @@ Style/SpaceInsideBlockBraces:
# No trailing space differentiates better from the block:
# foo} means hash, foo } means block.
Style/SpaceInsideHashLiteralBraces:
Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space
# { ... } for multi-line blocks is okay, follow Weirichs rule instead:
@ -165,7 +165,7 @@ Style/NumericPredicate:
EnforcedStyle: comparison
# Reset some HoundCI changes back to Rubocop defaults
Style/DotPosition:
Layout/DotPosition:
EnforcedStyle: leading
### backward compatibility

View file

@ -1 +1 @@
2.3
2.4

View file

@ -1,8 +1,8 @@
language: ruby
rvm:
- 2.3.1
- 2.1
- 2.4.1
- 2.3.4
env:
- DB=postgresql BUILD_TYPE=cucumber
@ -15,6 +15,7 @@ cache:
bundler: true
directories:
- app/assets/images
- tmp/cache/assets
branches:
only:
@ -29,7 +30,7 @@ before_install:
- tar -xvf $PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2 -C $PWD/travis-phantomjs
- export PATH=$PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64/bin:$PATH
bundler_args: "--deployment --without development production --with mysql postgresql --jobs 3 --retry 3"
bundler_args: "--deployment --without development --with mysql postgresql --jobs 3 --retry 3"
script: "./script/ci/build.sh"

42
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,42 @@
# Contributing to diaspora\*
First of all: thank you very much for helping us out!
## Things you need to know before contributing
If you want to get in touch with other diaspora\* developers, [check our wiki][how-we-communicate] for information on how we communicate. Feel free to ask if you have any questions!
Everyone interacting with our code, issue trackers, chat rooms, mailing lists, the wiki, and the discourse forum is expected to follow the [diaspora\* code of conduct][code-of-conduct].
## Report a security issue
Found a security issue? Please disclose it responsibly. We have a team of developers listening to [security@diasporafoundation.org][sec-mail]. The PGP fingerprint is [AB0D AB02 0FC5 D398 03AB 3CE1 6F70 243F 27AD 886A][pgp].
## Contributing to translations
We use [WebTranslateIt][webtranslateit] to manage translations of the app interface. Please read [our wiki page][translation-wiki] to find out more about this. If your language is featured on WebTranslateIt, please do **not** open a pull request to update translations. If it is not already featured on WebTranslateIt, please read the wiki article above to find out how to proceed.
## Contributing to the code
**Before opening a pull request** please read [how to contribute][contribute]. Doing things the right way from the start will save us time and mean that your contribution can be integrated quicker!
- Follow our [git workflow][git-workflow] method. In particular, *do not* open a pull request from the `master` or the `develop` branch.
- Follow our [styleguide][styleguide] and run pronto, our syntax analyzer, **locally before opening a pull request**. See [our wiki][pronto] for information on how to do this.
- [Write tests][testing-workflow].
- Use meaningful commit-messages and split larger tasks, e.g. refactoring, into separate commits. This makes the review process much easier.
## Other ways to contribute
You dont know code? No worries, there are plenty other ways to help the diaspora* project! Please find out how you can help [on our wiki][other-ways].
[code-of-conduct]: https://github.com/diaspora/diaspora/blob/develop/CODE_OF_CONDUCT.md
[how-we-communicate]: https://wiki.diasporafoundation.org/How_we_communicate
[pgp]: https://pgp.mit.edu/pks/lookup?op=get&search=0x6F70243F27AD886A
[sec-mail]: mailto:security@diasporafoundation.org
[webtranslateit]: https://webtranslateit.com/en/projects/3020-Diaspora
[translation-wiki]: https://wiki.diasporafoundation.org/Contribute_translations
[contribute]: https://wiki.diasporafoundation.org/Getting_started_with_contributing
[git-workflow]: https://wiki.diasporafoundation.org/Git_Workflow
[styleguide]: https://wiki.diasporafoundation.org/Styleguide
[pronto]: https://wiki.diasporafoundation.org/Styleguide#Automatic_local_review
[testing-workflow]: https://wiki.diasporafoundation.org/Testing_Workflow
[other-ways]: https://wiki.diasporafoundation.org/Other_ways_to_contribute

View file

@ -1,3 +1,63 @@
# 0.7.0.0
## Supported Ruby versions
This release recommends using Ruby 2.4, while retaining Ruby 2.3 as an officially supported version.
Ruby 2.1 is no longer officially supported.
## Delete public/.well-known/
Before upgrading, please check if your `public/` folder contains a hidden `.well-known/` folder.
If so, please delete it since it will prevent the federation from working properly.
## Refactor
* Make the mention syntax more flexible [#7305](https://github.com/diaspora/diaspora/pull/7305)
* Display @ before mentions [#7324](https://github.com/diaspora/diaspora/pull/7324)
* Simplify mentions in the publisher [#7302](https://github.com/diaspora/diaspora/pull/7302)
* Remove chartbeat and mixpanel support [#7280](https://github.com/diaspora/diaspora/pull/7280)
* Upgrade to jQuery 3 [#7303](https://github.com/diaspora/diaspora/pull/7303)
* Add i18n for color themes [#7369](https://github.com/diaspora/diaspora/pull/7369)
* Remove deprecated statistics.json [#7399](https://github.com/diaspora/diaspora/pull/7399)
* Always link comment count text on mobile [#7483](https://github.com/diaspora/diaspora/pull/7483)
* Switch to new federation protocol [#7436](https://github.com/diaspora/diaspora/pull/7436)
* Send public profiles publicly [#7501](https://github.com/diaspora/diaspora/pull/7501)
* Change sender for mails [#7495](https://github.com/diaspora/diaspora/pull/7495)
* Move back to top to the right to avoid misclicks [#7516](https://github.com/diaspora/diaspora/pull/7516)
* Include count in mobile post action link [#7520](https://github.com/diaspora/diaspora/pull/7520)
* Update the user data export archive format [#6726](https://github.com/diaspora/diaspora/pull/6726)
* Use id as fallback when sorting posts [#7523](https://github.com/diaspora/diaspora/pull/7523)
* Remove no-posts-info when adding posts to the stream [#7523](https://github.com/diaspora/diaspora/pull/7523)
* Upgrade to rails 5.1 [#7514](https://github.com/diaspora/diaspora/pull/7514)
* Refactoring single post view interactions [#7182](https://github.com/diaspora/diaspora/pull/7182)
* Update help pages [#7528](https://github.com/diaspora/diaspora/pull/7528)
* Disable rendering logging in production [#7529](https://github.com/diaspora/diaspora/pull/7529)
* Add some missing indexes and cleanup the database if needed [#7533](https://github.com/diaspora/diaspora/pull/7533)
* Remove avatar, name, timestamp and interactions from publisher preview [#7536](https://github.com/diaspora/diaspora/pull/7536)
## Bug fixes
* Fix height too high on mobile SPV [#7480](https://github.com/diaspora/diaspora/pull/7480)
* Improve stream when ignoring a person who posts a lot of tagged posts [#7503](https://github.com/diaspora/diaspora/pull/7503)
* Fix order of comments across pods [#7436](https://github.com/diaspora/diaspora/pull/7436)
* Prevent publisher from closing in preview mode [#7518](https://github.com/diaspora/diaspora/pull/7518)
* Increase reshare counter after reshare on mobile [#7520](https://github.com/diaspora/diaspora/pull/7520)
* Reset stuck exports and handle errors [#7535](https://github.com/diaspora/diaspora/pull/7535)
## Features
* Add support for mentions in comments to the backend [#6818](https://github.com/diaspora/diaspora/pull/6818)
* Add support for new mention syntax [#7300](https://github.com/diaspora/diaspora/pull/7300) [#7394](https://github.com/diaspora/diaspora/pull/7394)
* Render mentions as links in comments [#7327](https://github.com/diaspora/diaspora/pull/7327)
* Add support for mentions in comments to the front-end [#7386](https://github.com/diaspora/diaspora/pull/7386)
* Support direct links to comments on mobile [#7508](https://github.com/diaspora/diaspora/pull/7508)
* Add inviter first and last name in the invitation e-mail [#7484](https://github.com/diaspora/diaspora/pull/7484)
* Add markdown editor for comments and conversations [#7482](https://github.com/diaspora/diaspora/pull/7482)
* Improve responsive header in desktop version [#7509](https://github.com/diaspora/diaspora/pull/7509)
* Support cmd+enter to submit posts, comments and conversations [#7524](https://github.com/diaspora/diaspora/pull/7524)
* Add markdown editor for posts, comments and conversations on mobile [#7235](https://github.com/diaspora/diaspora/pull/7235)
* Mark as "Mobile Web App Capable" on Android [#7534](https://github.com/diaspora/diaspora/pull/7534)
* Add support for receiving account migrations [#6750](https://github.com/diaspora/diaspora/pull/6750)
# 0.6.7.0
## Refactor

151
Gemfile
View file

@ -1,29 +1,30 @@
source "https://rubygems.org"
gem "rails", "4.2.8"
gem "rails", "5.1.3"
# Legacy Rails features, remove me!
# responders (class level)
gem "responders", "2.3.0"
gem "responders", "2.4.0"
# Appserver
gem "unicorn", "5.2.0", require: false
gem "unicorn", "5.3.0", require: false
gem "unicorn-worker-killer", "0.4.4"
# Federation
gem "diaspora_federation-rails", "0.1.9"
gem "diaspora_federation-json_schema", "0.2.1"
gem "diaspora_federation-rails", "0.2.1"
# API and JSON
gem "acts_as_api", "1.0.0"
gem "json", "1.8.6"
gem "acts_as_api", "1.0.1"
gem "json", "2.1.0"
gem "json-schema", "2.8.0"
# Authentication
gem "devise", "4.2.0"
gem "devise", "4.3.0"
gem "devise_lastseenable", "0.0.6"
# Captcha
@ -32,15 +33,15 @@ gem "simple_captcha2", "0.4.3", require: "simple_captcha"
# Background processing
gem "sidekiq", "4.2.9"
gem "sidekiq", "5.0.4"
# Scheduled processing
gem "sidekiq-cron", "0.4.5"
gem "sidekiq-cron", "0.6.3"
# Compression
gem "uglifier", "3.1.2"
gem "uglifier", "3.2.0"
# Configuration
@ -48,33 +49,34 @@ gem "configurate", "0.3.1"
# Cross-origin resource sharing
gem "rack-cors", "0.4.1", require: "rack/cors"
gem "rack-cors", "1.0.1", require: "rack/cors"
# CSS
gem "autoprefixer-rails", "6.7.6"
gem "autoprefixer-rails", "7.1.2.4"
gem "bootstrap-sass", "3.3.7"
gem "bootstrap-switch-rails", "3.3.3"
gem "compass-rails", "2.0.5"
gem "compass-rails", "3.0.2"
gem "sass-rails", "5.0.6"
gem "sprockets-rails", "3.2.0"
# Database
group :mysql, optional: true do
gem "mysql2", "0.4.5"
gem "mysql2", "0.4.9"
end
group :postgresql, optional: true do
gem "pg", "0.19.0"
gem "pg", "0.21.0"
end
gem "activerecord-import", "0.17.1"
gem "activerecord-import", "0.19.1"
# File uploading
gem "fog-aws", "1.2.1"
gem "carrierwave", "0.11.2"
gem "mini_magick", "4.6.1"
gem "carrierwave", "1.1.0"
gem "fog-aws", "1.4.0"
gem "mini_magick", "4.8.0"
# GUID generation
gem "uuid", "2.3.8"
@ -85,19 +87,19 @@ gem "entypo-rails", "3.0.0"
# JavaScript
gem "handlebars_assets", "0.23.1"
gem "jquery-rails", "4.2.1"
gem "jquery-ui-rails", "5.0.5"
gem "handlebars_assets", "0.23.2"
gem "jquery-rails", "4.3.1"
gem "js-routes", "1.3.3"
gem "js_image_paths", "0.1.0"
gem "js_image_paths", "0.1.1"
source "https://rails-assets.org" do
gem "rails-assets-jquery", "2.2.4" # Should be kept in sync with jquery-rails
gem "rails-assets-jquery", "3.2.1" # Should be kept in sync with jquery-rails
gem "rails-assets-jquery.ui", "1.11.4"
gem "rails-assets-highlightjs", "9.9.0"
gem "rails-assets-markdown-it", "8.3.0"
gem "rails-assets-highlightjs", "9.12.0"
gem "rails-assets-markdown-it", "8.3.2"
gem "rails-assets-markdown-it-hashtag", "0.4.0"
gem "rails-assets-markdown-it-diaspora-mention", "1.1.1"
gem "rails-assets-markdown-it-diaspora-mention", "1.2.0"
gem "rails-assets-markdown-it-sanitizer", "0.4.3"
gem "rails-assets-markdown-it--markdown-it-for-inline", "0.1.1"
gem "rails-assets-markdown-it-sub", "1.0.0"
@ -110,8 +112,8 @@ source "https://rails-assets.org" do
# jQuery plugins
gem "rails-assets-autosize", "3.0.20"
gem "rails-assets-blueimp-gallery", "2.25.0"
gem "rails-assets-autosize", "4.0.0"
gem "rails-assets-blueimp-gallery", "2.25.2"
gem "rails-assets-jquery.are-you-sure", "1.9.0"
gem "rails-assets-jquery-placeholder", "2.3.1"
gem "rails-assets-jquery-textchange", "0.2.3"
@ -120,24 +122,24 @@ end
# Localization
gem "http_accept_language", "2.1.0"
gem "http_accept_language", "2.1.1"
gem "i18n-inflector-rails", "1.0.7"
gem "rails-i18n", "4.0.8"
gem "rails-i18n", "5.0.4"
# Mail
gem "markerb", "1.1.0"
# Map
gem "leaflet-rails", "0.7.7"
gem "leaflet-rails", "1.1.0"
# Parsing
gem "nokogiri", "1.7.2"
gem "nokogiri", "1.8.0"
gem "open_graph_reader", "0.6.2" # also update User-Agent in features/support/webmock.rb
gem "redcarpet", "3.4.0"
gem "ruby-oembed", "0.10.1"
gem "twitter-text", "1.14.5"
gem "ruby-oembed", "0.12.0"
gem "twitter-text", "1.14.7"
# RTL support
@ -145,11 +147,11 @@ gem "string-direction", "1.2.0"
# Security Headers
gem "secure_headers", "3.6.1"
gem "secure_headers", "3.6.7"
# Services
gem "omniauth", "1.4.2"
gem "omniauth", "1.6.1"
gem "omniauth-facebook", "4.0.0"
gem "omniauth-tumblr", "1.2"
gem "omniauth-twitter", "1.4.0"
@ -157,11 +159,11 @@ gem "twitter", "6.1.0"
gem "omniauth-wordpress", "0.2.2"
# OpenID Connect
gem "openid_connect", "0.12.0"
gem "openid_connect", "1.1.3"
# Serializers
gem "active_model_serializers", "0.9.5"
gem "active_model_serializers", "0.9.7"
# XMPP chat dependencies
gem "diaspora-prosody-config", "0.0.7"
@ -169,11 +171,11 @@ gem "rails-assets-diaspora_jsxc", "0.1.5.develop.7", source: "https://rails-asse
# Tags
gem "acts-as-taggable-on", "3.5.0"
gem "acts-as-taggable-on", "5.0.0"
# URIs and HTTP
gem "addressable", "2.5.0", require: "addressable/uri"
gem "addressable", "2.5.1", require: "addressable/uri"
gem "faraday", "0.11.0" # also update User-Agent in OpenID specs
gem "faraday_middleware", "0.11.0.1"
gem "faraday-cookie_jar", "0.0.6"
@ -182,14 +184,14 @@ gem "typhoeus", "1.1.2"
# Views
gem "gon", "6.1.0"
gem "hamlit", "2.8.0"
gem "mobile-fu", "1.3.1"
gem "hamlit", "2.8.4"
gem "mobile_fu", "1.4.0", require: "mobile-fu"
gem "rails-timeago", "2.16.0"
gem "will_paginate", "3.1.5"
gem "will_paginate", "3.1.6"
# Logging
gem "logging-rails", "0.5.0", require: "logging/rails"
gem "logging-rails", "0.6.0", require: "logging/rails"
# Reading and writing zip files
@ -202,13 +204,7 @@ gem "rubyzip", "1.2.1", require: "zip"
# https://github.com/discourse/discourse/pull/238
gem "minitest"
gem "versionist", "1.5.0"
# Prevent accidental upgrades of thor
# TODO: remove this when either all gems depending on thor have fixed the warnings
# or thor released a version where they are clearly marked as warnings
# see: https://github.com/erikhuda/thor/issues/538
gem "thor", "0.19.1"
gem "versionist", "1.6.0"
# Windows and OSX have an execjs compatible runtime built-in, Linux users should
# install Node.js or use "therubyracer".
@ -225,7 +221,7 @@ group :production do # we don"t install these on travis to speed up test runs
# Process management
gem "eye", "0.9.1"
gem "eye", "0.9.2"
# Redirects
@ -234,7 +230,7 @@ group :production do # we don"t install these on travis to speed up test runs
# Third party asset hosting
gem "asset_sync", "2.0.0", require: false
gem "asset_sync", "2.2.0", require: false
end
group :development do
@ -242,22 +238,22 @@ group :development do
gem "guard", "2.14.1", require: false
gem "guard-cucumber", "2.1.2", require: false
gem "guard-rspec", "4.7.3", require: false
gem "guard-rubocop", "1.2.0", require: false
gem "rb-fsevent", "0.9.8", require: false
gem "rb-inotify", "0.9.8", require: false
gem "guard-rubocop", "1.3.0", require: false
gem "rb-fsevent", "0.10.2", require: false
gem "rb-inotify", "0.9.10", require: false
# Linters
gem "haml_lint", "0.24.0", require: false
gem "pronto", "0.9.3", require: false
gem "haml_lint", "0.26.0", require: false
gem "pronto", "0.9.4", require: false
gem "pronto-eslint", "0.9.1", require: false
gem "pronto-haml", "0.9.0", require: false
gem "pronto-rubocop", "0.9.0", require: false
gem "pronto-scss", "0.9.1", require: false
gem "rubocop", "0.48.1", require: false
gem "rubocop", "0.49.1", require: false
# Preloading environment
gem "spring", "2.0.1"
gem "spring", "2.0.2"
gem "spring-commands-rspec", "1.0.4"
gem "spring-commands-cucumber", "1.0.1"
@ -266,7 +262,7 @@ group :development do
gem "pry-byebug"
# test coverage
gem "simplecov", "0.12.0", require: false
gem "simplecov", "0.14.1", require: false
gem "turbo_dev_assets", "0.0.2"
end
@ -274,44 +270,45 @@ end
group :test do
# RSpec (unit tests, some integration tests)
gem "fixture_builder", "0.4.1"
gem "fixture_builder", "0.5.0"
gem "fuubar", "2.2.0"
gem "test_after_commit", "1.1.0"
gem "json-schema-rspec", "0.0.4"
gem "rspec-json_expectations", "~> 2.1"
# Cucumber (integration tests)
gem "capybara", "2.12.1"
gem "database_cleaner", "1.5.3"
gem "poltergeist", "1.13.0"
gem "capybara", "2.15.1"
gem "database_cleaner", "1.6.1"
gem "poltergeist", "1.16.0"
gem "cucumber-api-steps", "0.13", require: false
# General helpers
gem "factory_girl_rails", "4.8.0"
gem "timecop", "0.8.1"
gem "webmock", "2.3.2", require: false
gem "shoulda-matchers", "3.1.1"
gem "shoulda-matchers", "3.1.2"
gem "timecop", "0.9.1"
gem "webmock", "3.0.1", require: false
gem "diaspora_federation-test", "0.1.9"
gem "diaspora_federation-test", "0.2.1"
# Coverage
gem 'coveralls', require: false
gem "coveralls", "0.8.21", require: false
end
group :development, :test do
# RSpec (unit tests, some integration tests)
gem "rspec-rails", "3.5.2"
gem "rspec-rails", "3.6.1"
# Cucumber (integration tests)
gem "cucumber-rails", "1.4.5", require: false
gem "cucumber-rails", "1.5.0", require: false
# Jasmine (client side application tests (JS))
gem "jasmine", "2.5.2"
gem "jasmine", "2.7.0"
gem "jasmine-jquery-rails", "2.0.3"
gem "rails-assets-jasmine-ajax", "3.3.1", source: "https://rails-assets.org"
gem "sinon-rails", "1.15.0"
# silence assets
gem "quiet_assets", "1.1.0"
# For `assigns` in controller specs
gem "rails-controller-testing", "1.0.2"
end

File diff suppressed because it is too large Load diff

View file

@ -121,12 +121,6 @@ var app = {
app.flashMessages = new app.views.FlashMessages({el: $("#flash-container")});
},
/* mixpanel wrapper function */
instrument : function(type, name, object, callback) {
if(!window.mixpanel) { return; }
window.mixpanel[type](name, object, callback);
},
setupDisabledLinks: function() {
$("a.disabled").click(function(event) {
event.preventDefault();

View file

@ -48,12 +48,6 @@ app.models.Post = Backbone.Model.extend(_.extend({}, app.models.formatDateMixin,
var body = this.get("text").trim()
, newlineIdx = body.indexOf("\n");
return (newlineIdx > 0 ) ? body.substr(newlineIdx+1, body.length) : "";
},
//returns a promise
preloadOrFetch : function(){
var action = app.hasPreload("post") ? this.set(app.parsePreload("post")) : this.fetch();
return $.when(action);
}
}));
// @license-end

View file

@ -3,10 +3,6 @@
//require ../post
app.models.Post.Interactions = Backbone.Model.extend({
url : function(){
return this.post.url() + "/interactions";
},
initialize : function(options){
this.post = options.post;
this.comments = new app.collections.Comments(this.get("comments"), {post : this.post});
@ -14,33 +10,16 @@ app.models.Post.Interactions = Backbone.Model.extend({
this.reshares = new app.collections.Reshares(this.get("reshares"), {post : this.post});
},
parse : function(resp){
this.comments.reset(resp.comments);
this.likes.reset(resp.likes);
this.reshares.reset(resp.reshares);
var comments = this.comments
, likes = this.likes
, reshares = this.reshares;
return {
comments : comments,
likes : likes,
reshares : reshares,
fetched : true
};
},
likesCount : function(){
return this.get("fetched") ? this.likes.models.length : this.get("likes_count");
return this.get("likes_count");
},
resharesCount : function(){
return this.get("fetched") ? this.reshares.models.length : this.get("reshares_count");
return this.get("reshares_count");
},
commentsCount : function(){
return this.get("fetched") ? this.comments.models.length : this.get("comments_count");
return this.get("comments_count");
},
userLike : function(){
@ -76,8 +55,6 @@ app.models.Post.Interactions = Backbone.Model.extend({
app.flashMessages.handleAjaxError(response);
}
});
app.instrument("track", "Like");
},
unlike : function() {
@ -87,8 +64,6 @@ app.models.Post.Interactions = Backbone.Model.extend({
self.set({"likes_count" : self.get("likes_count") - 1});
self.likes.trigger("change");
}});
app.instrument("track", "Unlike");
},
comment: function(text, options) {
@ -104,8 +79,6 @@ app.models.Post.Interactions = Backbone.Model.extend({
self.trigger('change'); //updates after sync
if (options.success) { options.success(); }
});
app.instrument("track", "Comment");
},
reshare : function(){
@ -126,8 +99,6 @@ app.models.Post.Interactions = Backbone.Model.extend({
.fail(function(response) {
app.flashMessages.handleAjaxError(response);
});
app.instrument("track", "Reshare");
},
userCanReshare : function(){

View file

@ -13,8 +13,16 @@ app.models.Stream = Backbone.Collection.extend({
},
collectionOptions :function(){
var order = this.sortOrder();
return { comparator : function(item) { return -item[order](); } };
var order = this.sortOrder();
return {
comparator: function(item1, item2) {
if (item1[order]() < item2[order]()) { return 1; }
if (item1[order]() > item2[order]()) { return -1; }
if (item1.id < item2.id) { return 1; }
if (item1.id > item2.id) { return -1; }
return 0;
}
};
},
url : function(){

View file

@ -9,9 +9,8 @@ app.pages.SinglePostViewer = app.views.Base.extend({
},
initialize : function() {
this.model = new app.models.Post({ id : gon.post.id });
this.model.preloadOrFetch().done(_.bind(this.initViews, this));
this.model.interactions.fetch(); //async, yo, might want to throttle this later.
this.model = new app.models.Post(gon.post);
this.initViews();
},
initViews : function() {

View file

@ -9,7 +9,7 @@ app.Router = Backbone.Router.extend({
"commented(/)": "stream",
"community_spotlight(/)": "spotlight",
"contacts(/)": "contacts",
"conversations(/)(:id)(/)": "conversations",
"conversations(/)(:id)(?conversation_id=:conversation_id)(/)": "conversations",
"followed_tags(/)": "followed_tags",
"getting_started(/)": "gettingStarted",
"help(/)": "help",
@ -93,8 +93,8 @@ app.Router = Backbone.Router.extend({
app.page = new app.pages.Contacts({stream: stream});
},
conversations: function(id) {
app.conversations = app.conversations || new app.views.ConversationsInbox();
conversations: function(id, conversationId) {
app.conversations = app.conversations || new app.views.ConversationsInbox(conversationId);
if (parseInt("" + id, 10)) {
app.conversations.renderConversation(id);
}
@ -182,7 +182,7 @@ app.Router = Backbone.Router.extend({
},
singlePost: function(id) {
this.renderPage(function() { return new app.pages.SinglePostViewer({id: id}); });
this.renderPage(function() { return new app.pages.SinglePostViewer({id: id, el: $("#container")}); });
},
spotlight: function() {

View file

@ -0,0 +1,10 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
//= require ./publisher/mention_view
app.views.CommentMention = app.views.PublisherMention.extend({
initialize: function(opts) {
opts.url = Routes.mentionablePost(opts.postId);
app.views.PublisherMention.prototype.initialize.call(this, opts);
}
});
// @license-end

View file

@ -9,8 +9,8 @@ app.views.CommentStream = app.views.Base.extend({
events: {
"keydown .comment_box": "keyDownOnCommentBox",
"submit form": "createComment",
"focus .comment_box": "commentTextareaFocused",
"click .toggle_post_comments": "expandComments"
"click .toggle_post_comments": "expandComments",
"click form": "openForm"
},
initialize: function() {
@ -21,12 +21,22 @@ app.views.CommentStream = app.views.Base.extend({
setupBindings: function() {
this.model.comments.bind("add", this.appendComment, this);
this.model.comments.bind("remove", this.removeComment, this);
$(document.body).click(this.onFormBlur.bind(this));
},
postRenderTemplate : function() {
this.model.comments.each(this.appendComment, this);
this.commentBox = this.$(".comment_box");
this.commentSubmitButton = this.$("input[name='commit']");
this.mentions = new app.views.CommentMention({el: this.$el, postId: this.model.get("id")});
this.mdEditor = new Diaspora.MarkdownEditor(this.$(".comment_box"), {
onPreview: function($mdInstance) {
var renderedText = app.helpers.textFormatter($mdInstance.getContent(), this.mentions.getMentionedPeople());
return "<div class='preview-content'>" + renderedText + "</div>";
}.bind(this),
onFocus: this.openForm.bind(this)
});
},
presenter: function(){
@ -52,11 +62,14 @@ app.views.CommentStream = app.views.Base.extend({
success: function() {
this.commentBox.val("");
this.enableCommentBox();
this.mdEditor.hidePreview();
this.closeForm();
autosize.update(this.commentBox);
}.bind(this),
error: function() {
this.enableCommentBox();
this.commentBox.focus();
this.mdEditor.hidePreview();
this.openForm();
}.bind(this)
});
},
@ -72,7 +85,7 @@ app.views.CommentStream = app.views.Base.extend({
},
keyDownOnCommentBox: function(evt) {
if(evt.which === Keycodes.ENTER && evt.ctrlKey) {
if (evt.which === Keycodes.ENTER && (evt.metaKey || evt.ctrlKey)) {
this.$("form").submit();
return false;
}
@ -105,7 +118,8 @@ app.views.CommentStream = app.views.Base.extend({
// on post ownership in the Comment view.
comment.set({parent : this.model.toJSON()});
var commentHtml = new this.CommentView({model: comment}).render().el;
var commentView = new this.CommentView({model: comment});
var commentHtml = commentView.render().el;
var commentBlocks = this.$(".comments div.comment.media");
this._moveInsertPoint(comment.get("created_at"), commentBlocks);
if (this._insertPoint >= commentBlocks.length) {
@ -115,16 +129,13 @@ app.views.CommentStream = app.views.Base.extend({
} else {
commentBlocks.eq(this._insertPoint).before(commentHtml);
}
commentView.renderPluginWidgets();
},
removeComment: function(comment) {
this.$("#" + comment.get("guid")).closest(".comment.media").remove();
},
commentTextareaFocused: function(){
this.$("form").removeClass('hidden').addClass("open");
},
expandComments: function(evt){
this.$(".loading-comments").removeClass("hidden");
if(evt){ evt.preventDefault(); }
@ -134,6 +145,37 @@ app.views.CommentStream = app.views.Base.extend({
this.$(".loading-comments").addClass("hidden");
}.bind(this)
});
},
openForm: function() {
this.$("form").addClass("open");
this.$(".md-editor").addClass("active");
},
closeForm: function() {
this.$("form").removeClass("open");
this.$(".md-editor").removeClass("active");
this.commentBox.blur();
autosize.update(this.commentBox);
},
isCloseAllowed: function() {
if (this.mdEditor === undefined) {
return true;
}
return !this.mdEditor.isPreviewMode() && this.mdEditor.userInputEmpty();
},
onFormBlur: function(evt) {
if (!this.isCloseAllowed()) {
return;
}
var $target = $(evt.target);
var isForm = $target.hasClass("new-comment") || $target.parents(".new-comment").length !== 0;
if (!isForm && !$target.hasClass("focus_comment_textarea")) {
this.closeForm();
}
}
});
// @license-end

View file

@ -4,6 +4,7 @@
app.views.Comment = app.views.Content.extend({
templateName: "comment",
className : "comment media",
tooltipSelector: "time",
events : function() {
return _.extend({}, app.views.Content.prototype.events, {
@ -20,7 +21,7 @@ app.views.Comment = app.views.Content.extend({
presenter : function() {
return _.extend(this.defaultPresenter(), {
canRemove: this.canRemove(),
text : app.helpers.textFormatter(this.model.get("text"))
text: app.helpers.textFormatter(this.model.get("text"), this.model.get("mentioned_people"))
});
},

View file

@ -17,15 +17,14 @@ app.views.Content = app.views.Base.extend({
largePhoto : function() {
var photos = this.model.get("photos");
if(!photos || photos.length === 0) { return }
if (!photos || photos.length === 0) { return false; }
return photos[0];
},
smallPhotos : function() {
var photos = this.model.get("photos");
if(!photos || photos.length < 2) { return }
photos.splice(0, 1); // remove first photo as it is already shown as largePhoto
return photos;
if (!photos || photos.length < 2) { return false; }
return photos.slice(1); // remove first photo as it is already shown as largePhoto
},
expandPost: function(evt) {

View file

@ -24,6 +24,8 @@ app.views.ConversationsForm = app.views.Base.extend({
remoteRoute: {url: "/contacts", extraParameters: "mutual=true"}
});
this.newConversationMdEditor = this.renderMarkdownEditor("#new-message-text");
this.bindTypeaheadEvents();
this.tagListElement.empty();
@ -31,10 +33,16 @@ app.views.ConversationsForm = app.views.Base.extend({
this.prefill(opts.prefill);
}
this.$("form#new-conversation").on("ajax:success", this.conversationCreateSuccess);
this.$("form#new-conversation").on("ajax:success", this.conversationCreateSuccess.bind(this));
this.$("form#new-conversation").on("ajax:error", this.conversationCreateError);
},
renderMarkdownEditor: function(element) {
return new Diaspora.MarkdownEditor($(element), {
onPreview: Diaspora.MarkdownEditor.simplePreview
});
},
addRecipient: function(person) {
this.conversationRecipients.push(person);
this.updateContactIdsListInput();
@ -66,7 +74,7 @@ app.views.ConversationsForm = app.views.Base.extend({
},
keyDown: function(evt) {
if (evt.which === Keycodes.ENTER && evt.ctrlKey) {
if (evt.which === Keycodes.ENTER && (evt.metaKey || evt.ctrlKey)) {
$(evt.target).parents("form").submit();
}
},
@ -84,6 +92,7 @@ app.views.ConversationsForm = app.views.Base.extend({
},
conversationCreateSuccess: function(evt, data) {
this.newConversationMdEditor.hidePreview();
app._changeLocation(Routes.conversation(data.id));
},

View file

@ -8,11 +8,21 @@ app.views.ConversationsInbox = app.views.Base.extend({
"click .new-conversation-btn": "displayNewConversation"
},
initialize: function() {
new app.views.ConversationsForm();
initialize: function(conversationId) {
this.conversationForm = new app.views.ConversationsForm();
// Creates markdown editor in case of displaying preloaded conversation
if (conversationId != null) {
this.renderMarkdownEditor();
}
this.setupConversation();
},
renderMarkdownEditor: function() {
this.conversationForm.renderMarkdownEditor("#conversation-show .conversation-message-text");
},
renderConversation: function(conversationId) {
var self = this;
$.ajax({
@ -23,6 +33,8 @@ app.views.ConversationsInbox = app.views.Base.extend({
self.$el.find("#conversation-show").removeClass("hidden").html(data);
self.selectConversation(conversationId);
self.setupConversation();
self.renderMarkdownEditor();
autosize(self.$("#conversation-show textarea"));
}
});
},

View file

@ -4,27 +4,29 @@ app.views.Help = app.views.StaticContentView.extend({
templateName : "help",
events : {
"click .faq-link" : "sectionClicked",
"click .faq-link-getting-help" : "gettingHelp",
"click .faq-link-sharing" : "sharing",
"click .faq-link-posts-and-posting" : "postsAndPosting",
"click .faq-link": "sectionClicked",
"click .faq-link-getting-help": "gettingHelp",
"click .faq-link-sharing": "sharing",
"click .faq-link-posts-and-posting": "postsAndPosting",
"click .faq-link-tags": "tags",
"click .faq-link-keyboard-shortcuts" : "keyboardShortcuts",
"click .faq-link-chat" : "chat"
"click .faq-link-keyboard-shortcuts": "keyboardShortcuts",
"click .faq-link-chat": "chat"
},
initialize : function() {
this.GETTING_HELP_SUBS = {
getting_started_a: { tutorial_series: this.linkHtml("http://diasporafoundation.org/getting_started/sign_up", Diaspora.I18n.t( 'getting_started_tutorial' )) },
get_support_a_website: { link: this.linkHtml("https://diasporafoundation.org/", Diaspora.I18n.t( 'foundation_website' ))},
get_support_a_tutorials: { tutorials: this.linkHtml("https://diasporafoundation.org/tutorials", Diaspora.I18n.t( 'tutorials' ))},
get_support_a_wiki: { link: this.linkHtml("https://wiki.diasporafoundation.org/Special:Search", Diaspora.I18n.t( 'wiki' ))},
get_support_a_irc: { irc: this.linkHtml("https://wiki.diasporafoundation.org/How_We_Communicate#IRC", Diaspora.I18n.t( 'irc' ))},
get_support_a_faq: { faq: this.linkHtml("https://wiki.diasporafoundation.org/FAQ_for_users", Diaspora.I18n.t( 'faq' ))},
get_support_a_hashtag: { question: this.linkHtml("/tags/question", "#question")}
getting_started_a: {tutorial_series: this.linkHtml("http://diasporafoundation.org/getting_started/sign_up", Diaspora.I18n.t("getting_started_tutorial"))},
get_support_a_website: {link: this.linkHtml("https://diasporafoundation.org/", Diaspora.I18n.t("foundation_website"))},
get_support_a_tutorials: {tutorials: this.linkHtml("https://diasporafoundation.org/tutorials", Diaspora.I18n.t("tutorials"))},
get_support_a_wiki: {link: this.linkHtml("https://wiki.diasporafoundation.org/Special:Search", Diaspora.I18n.t("wiki"))},
get_support_a_irc: {irc: this.linkHtml("https://wiki.diasporafoundation.org/How_We_Communicate#IRC", Diaspora.I18n.t("irc"))},
get_support_a_faq: {faq: this.linkHtml("https://wiki.diasporafoundation.org/FAQ_for_users", Diaspora.I18n.t("faq"))},
get_support_a_hashtag: {question: this.linkHtml("/tags/question", "#question")},
get_support_a_discourse: {discourse: this.linkHtml("https://discourse.diasporafoundation.org/c/support", "discussions & support")}
};
this.POSTS_AND_POSTING_SUBS = {
post_report_a: {community_guidelines: this.linkHtml("https://diasporafoundation.org/community_guidelines", Diaspora.I18n.t("community_guidelines"))},
format_text_a: {
markdown: this.linkHtml("http://diasporafoundation.org/formatting", Diaspora.I18n.t( 'markdown' )),
here: this.linkHtml("http://daringfireball.net/projects/markdown/syntax", Diaspora.I18n.t( 'here' ))
@ -53,10 +55,9 @@ app.views.Help = app.views.StaticContentView.extend({
title_pods: Diaspora.I18n.t( 'pods.title' ),
title_posts_and_posting: Diaspora.I18n.t( 'posts_and_posting.title' ),
title_private_posts: Diaspora.I18n.t( 'private_posts.title' ),
title_private_profiles: Diaspora.I18n.t( 'private_profiles.title' ),
title_public_posts: Diaspora.I18n.t( 'public_posts.title' ),
title_public_profiles: Diaspora.I18n.t( 'public_profiles.title' ),
title_resharing_posts: Diaspora.I18n.t( 'resharing_posts.title' ),
title_profile: Diaspora.I18n.t("profile.title"),
title_sharing: Diaspora.I18n.t( 'sharing.title' ),
title_tags: Diaspora.I18n.t( 'tags.title' ),
title_keyboard_shortcuts: Diaspora.I18n.t( 'keyboard_shortcuts.title' ),

View file

@ -51,6 +51,12 @@ app.views.InfScroll = app.views.Base.extend({
}
},
postRenderTemplate: function() {
if (this.postViews.length > 0) {
this.$(".no-posts-info").closest(".stream-element").remove();
}
},
showNoPostsInfo: function() {
if (this.postViews.length === 0) {
var noPostsInfo = new app.views.NoPostsInfo();

View file

@ -26,7 +26,6 @@ app.views.NotificationDropdown = app.views.Base.extend({
toggleDropdown: function(evt){
evt.stopPropagation();
if (!$("#notifications-link .entypo-bell:visible").length) { return true; }
evt.preventDefault();
if(this.dropdownShowing()){ this.hideDropdown(evt); }
else{ this.showDropdown(); }

View file

@ -5,7 +5,6 @@ app.views.PreviewPost = app.views.Post.extend({
className: "stream-element loaded",
subviews: {
".feedback": "feedbackView",
".post-content": "postContentView",
".oembed": "oEmbedView",
".opengraph": "openGraphView",
@ -13,12 +12,6 @@ app.views.PreviewPost = app.views.Post.extend({
".status-message-location": "postLocationStreamView"
},
tooltipSelector: [
".timeago",
".delete",
".permalink"
].join(", "),
initialize: function() {
this.model.set("preview", true);
this.oEmbedView = new app.views.OEmbed({model: this.model});
@ -26,10 +19,6 @@ app.views.PreviewPost = app.views.Post.extend({
this.pollView = new app.views.Poll({model: this.model});
},
feedbackView: function() {
return new app.views.Feedback({model: this.model});
},
postContentView: function() {
return new app.views.StatusMessage({model: this.model});
},

View file

@ -2,29 +2,20 @@
app.views.PublisherMention = app.views.SearchBase.extend({
triggerChar: "@",
invisibleChar: "\u200B", // zero width space
mentionRegex: /@([^@\s]+)$/,
templates: {
mentionItemSyntax: _.template("@{<%= name %> ; <%= handle %>}"),
mentionItemHighlight: _.template("<strong><span><%= name %></span></strong>")
},
mentionSyntaxTemplate: function(person) { return "@{" + person.handle + "}"; },
events: {
"keydown #status_message_fake_text": "onInputBoxKeyDown",
"input #status_message_fake_text": "onInputBoxInput",
"click #status_message_fake_text": "onInputBoxClick",
"blur #status_message_fake_text": "onInputBoxBlur"
"keydown .mention-textarea": "onInputBoxKeyDown",
"input .mention-textarea": "updateTypeaheadInput",
"click .mention-textarea": "onInputBoxClick",
"blur .mention-textarea": "onInputBoxBlur"
},
initialize: function() {
initialize: function(opts) {
this.mentionedPeople = [];
// contains the 'fake text' displayed to the user
// also has a data-messageText attribute with the original text
this.inputBox = this.$("#status_message_fake_text");
// contains the mentions displayed to the user
this.mentionsBox = this.$(".mentions-box");
var url = (opts && opts.url) || "/contacts";
this.inputBox = this.$(".mention-textarea");
this.typeaheadInput = this.$(".typeahead-mention-box");
this.bindTypeaheadEvents();
@ -32,7 +23,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({
typeaheadInput: this.typeaheadInput,
customSearch: true,
autoselect: true,
remoteRoute: {url: "/contacts"}
remoteRoute: {url: url}
});
},
@ -55,8 +46,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({
cleanMentionedPeople: function() {
var inputText = this.inputBox.val();
this.mentionedPeople = this.mentionedPeople.filter(function(person) {
return person.name && inputText.indexOf(person.name) > -1;
});
return person.handle && inputText.indexOf(this.mentionSyntaxTemplate(person)) > -1;
}.bind(this));
this.ignoreDiasporaIds = this.mentionedPeople.map(function(person) { return person.handle; });
},
@ -70,41 +61,16 @@ app.views.PublisherMention = app.views.SearchBase.extend({
this.addPersonToMentions(person);
this.closeSuggestions();
messageText = messageText.substring(0, triggerCharPosition) +
this.invisibleChar + person.name + messageText.substring(caretPosition);
var mentionText = this.mentionSyntaxTemplate(person);
messageText = messageText.substring(0, triggerCharPosition) + mentionText + messageText.substring(caretPosition);
this.inputBox.val(messageText);
this.updateMessageTexts();
this.inputBox.focus();
var newCaretPosition = triggerCharPosition + person.name.length + 1;
var newCaretPosition = triggerCharPosition + mentionText.length;
this.inputBox[0].setSelectionRange(newCaretPosition, newCaretPosition);
},
/**
* Replaces every combination of this.invisibleChar + mention.name by the
* correct syntax for both hidden text and visible one.
*
* For instance, the text "Hello \u200Buser1" will be tranformed to
* "Hello @{user1 ; user1@pod.tld}" in the hidden element and
* "Hello <strong><span>user1</span></strong>" in the element visible to the user.
*/
updateMessageTexts: function() {
var fakeMessageText = this.inputBox.val(),
mentionBoxText = _.escape(fakeMessageText),
messageText = fakeMessageText;
this.mentionedPeople.forEach(function(person) {
var mentionName = this.invisibleChar + person.name;
messageText = messageText.replace(mentionName, this.templates.mentionItemSyntax(person));
var textHighlight = this.templates.mentionItemHighlight({name: _.escape(person.name)});
mentionBoxText = mentionBoxText.replace(mentionName, textHighlight);
}, this);
this.inputBox.data("messageText", messageText);
this.mentionsBox.find(".mentions").html(mentionBoxText);
},
updateTypeaheadInput: function() {
var messageText = this.inputBox.val();
var caretPosition = this.inputBox[0].selectionStart;
@ -115,6 +81,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({
return;
}
this.cleanMentionedPeople();
// result[1] is the string between the last '@' and the current caret position
this.typeaheadInput.typeahead("val", result[1]);
this.typeaheadInput.typeahead("open");
@ -128,12 +96,11 @@ app.views.PublisherMention = app.views.SearchBase.extend({
prefillMention: function(persons) {
persons.forEach(function(person) {
this.addPersonToMentions(person);
var text = this.invisibleChar + person.name;
var text = this.mentionSyntaxTemplate(person);
if(this.inputBox.val().length !== 0) {
text = this.inputBox.val() + " " + text;
}
this.inputBox.val(text);
this.updateMessageTexts();
}, this);
},
@ -154,15 +121,6 @@ app.views.PublisherMention = app.views.SearchBase.extend({
this.typeaheadInput.trigger($.Event("keydown", {keyCode: e.keyCode, which: e.which}));
},
/**
* Listens for user input and opens results dropdown when input contains the trigger char
*/
onInputBoxInput: function() {
this.cleanMentionedPeople();
this.updateMessageTexts();
this.updateTypeaheadInput();
},
onInputBoxKeyDown: function(e) {
// This also matches HOME/END on OSX which is CMD+LEFT, CMD+RIGHT
if(e.which === Keycodes.LEFT || e.which === Keycodes.RIGHT ||
@ -205,7 +163,7 @@ app.views.PublisherMention = app.views.SearchBase.extend({
reset: function() {
this.inputBox.val("");
this.onInputBoxInput();
this.updateTypeaheadInput();
},
closeSuggestions: function() {
@ -217,7 +175,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({
return this.$(".tt-menu").is(":visible");
},
getTextForSubmit: function() {
return this.mentionedPeople.length ? this.inputBox.data("messageText") : this.inputBox.val();
getMentionedPeople: function() {
this.cleanMentionedPeople();
return this.mentionedPeople;
}
});

View file

@ -18,11 +18,11 @@ app.views.Publisher = Backbone.View.extend({
el : "#publisher",
events : {
"keydown #status_message_fake_text" : "keyDown",
"keydown #status_message_text": "keyDown",
"focus textarea" : "open",
"submit form" : "createStatusMessage",
"click #submit" : "createStatusMessage",
"textchange #status_message_fake_text": "handleTextchange",
"textchange #status_message_text": "checkSubmitAvailability",
"click #locator" : "showLocation",
"click #poll_creator" : "togglePollCreator",
"click #hide_location" : "destroyLocation",
@ -35,8 +35,7 @@ app.views.Publisher = Backbone.View.extend({
this.disabled = false;
// init shortcut references to the various elements
this.inputEl = this.$("#status_message_fake_text");
this.hiddenInputEl = this.$("#status_message_text");
this.inputEl = this.$("#status_message_text");
this.wrapperEl = this.$("#publisher_textarea_wrapper");
this.submitEl = this.$("input[type=submit], button#submit");
this.photozoneEl = this.$("#photodropzone");
@ -46,14 +45,6 @@ app.views.Publisher = Backbone.View.extend({
$(window).on("beforeunload", _.bind(this._beforeUnload, this));
$(window).on("unload", this.clear.bind(this));
// sync textarea content
if( this.hiddenInputEl.val() === "" ) {
this.hiddenInputEl.val( this.inputEl.val() );
}
if( this.inputEl.val() === "" ) {
this.inputEl.val( this.hiddenInputEl.val() );
}
// hide close and preview buttons and manage services link
// in case publisher is standalone
// (e.g. bookmarklet, mentions popup)
@ -163,7 +154,7 @@ app.views.Publisher = Backbone.View.extend({
this.viewPollCreator.render();
if (this.prefillMention) {
this.handleTextchange();
this.checkSubmitAvailability();
}
},
@ -175,12 +166,11 @@ app.views.Publisher = Backbone.View.extend({
// inject content into the publisher textarea
setText: function(txt) {
this.inputEl.val(txt);
this.hiddenInputEl.val(txt);
this.prefillText = txt;
this.inputEl.trigger("input");
autosize.update(this.inputEl);
this.handleTextchange();
this.checkSubmitAvailability();
},
// show the "getting started" popups around the publisher
@ -202,9 +192,6 @@ app.views.Publisher = Backbone.View.extend({
// empty poll answer and failing validation.
this.viewPollCreator.removeLastAnswer();
//add missing mentions at end of post:
this.handleTextchange();
var serializedForm = $(evt.target).closest("form").serializeObject();
// disable input while posting, must be after the form is serialized
this.setInputEnabled(false);
@ -255,7 +242,7 @@ app.views.Publisher = Backbone.View.extend({
self.setButtonsEnabled(true);
self.setInputEnabled(true);
self.wrapperEl.removeClass("submitting");
self.handleTextchange();
self.checkSubmitAvailability();
autosize.update(self.inputEl);
}
});
@ -330,13 +317,8 @@ app.views.Publisher = Backbone.View.extend({
},
createPostPreview: function() {
//add missing mentions at end of post:
this.handleTextchange();
var serializedForm = $("#new_status_message").serializeObject();
var text = this.mention.getTextForSubmit();
var photos = this.getUploadedPhotos();
var mentionedPeople = this.mention.mentionedPeople;
var poll = this.getPollData(serializedForm);
var locationCoords = serializedForm["location[coords]"];
if(!locationCoords || locationCoords === "") {
@ -352,12 +334,12 @@ app.views.Publisher = Backbone.View.extend({
var previewMessage = {
"id": 0,
"text": text,
"text": serializedForm["status_message[text]"],
"public": serializedForm["aspect_ids[]"] === "public",
"created_at": new Date().toISOString(),
"interacted_at": new Date().toISOString(),
"author": app.currentUser ? app.currentUser.attributes : {},
"mentioned_people": mentionedPeople,
"mentioned_people": this.mention.getMentionedPeople(),
"photos": photos,
"title": serializedForm["status_message[text]"],
"location": location,
@ -370,7 +352,7 @@ app.views.Publisher = Backbone.View.extend({
},
keyDown : function(evt) {
if(evt.which === Keycodes.ENTER && evt.ctrlKey) {
if (evt.which === Keycodes.ENTER && (evt.metaKey || evt.ctrlKey)) {
this.$("form").submit();
this.open();
return false;
@ -381,11 +363,10 @@ app.views.Publisher = Backbone.View.extend({
// remove mentions
this.mention.reset();
// clear text(s)
// clear text
this.inputEl.val("");
this.hiddenInputEl.val("");
this.inputEl.trigger("keyup")
.trigger("keydown");
.trigger("keydown");
autosize.update(this.inputEl);
// remove photos
@ -421,14 +402,13 @@ app.views.Publisher = Backbone.View.extend({
// force textchange plugin to update lastValue
this.inputEl.data("lastValue", "");
this.hiddenInputEl.data("lastValue", "");
return this;
},
tryClose : function(){
// if it is not submittable, close it.
if( !this._submittable() ){
// if it is not submittable and not in preview mode, close it.
if (!this._submittable() && !this.markdownEditor.isPreviewMode()) {
this.close();
}
},
@ -472,8 +452,7 @@ app.views.Publisher = Backbone.View.extend({
setEnabled: function(bool) {
this.setInputEnabled(bool);
this.disabled = !bool;
this.handleTextchange();
this.checkSubmitAvailability();
},
setButtonsEnabled: function(bool) {
@ -487,10 +466,8 @@ app.views.Publisher = Backbone.View.extend({
setInputEnabled: function(bool) {
if (bool) {
this.inputEl.removeAttr("disabled");
this.hiddenInputEl.removeAttr("disabled");
} else {
this.inputEl.prop("disabled", true);
this.hiddenInputEl.prop("disabled", true);
}
},
@ -503,11 +480,6 @@ app.views.Publisher = Backbone.View.extend({
return (!onlyWhitespaces || isPhotoAttached) && isValidPoll && !this.disabled;
},
handleTextchange: function() {
this.checkSubmitAvailability();
this.hiddenInputEl.val(this.mention.getTextForSubmit());
},
_beforeUnload: function(e) {
if(this._submittable() && this.inputEl.val() !== this.prefillText){
var confirmationMessage = Diaspora.I18n.t("confirm_unload");

View file

@ -7,7 +7,9 @@ app.views.SinglePostCommentStream = app.views.CommentStream.extend({
this.CommentView = app.views.ExpandedComment;
$(window).on('hashchange',this.highlightPermalinkComment);
this.setupBindings();
this.model.comments.on("reset", this.render, this);
this.model.comments.fetch({success: function() {
setTimeout(this.highlightPermalinkComment, 0);
}.bind(this)});
},
highlightPermalinkComment: function() {
@ -17,14 +19,13 @@ app.views.SinglePostCommentStream = app.views.CommentStream.extend({
$(".highlighted").removeClass("highlighted");
element.addClass("highlighted");
var pos = element.offset().top - headerSize;
window.scroll(0, pos);
$("html,body").animate({scrollTop: pos});
}
},
postRenderTemplate: function() {
app.views.CommentStream.prototype.postRenderTemplate.apply(this);
this.$(".new-comment-form-wrapper").removeClass("hidden");
_.defer(this.highlightPermalinkComment);
},
presenter: function(){

View file

@ -4,8 +4,15 @@ app.views.SinglePostInteractionCounts = app.views.Base.extend({
templateName: "single-post-viewer/single-post-interaction-counts",
tooltipSelector: ".avatar.micro",
events: {
"click #show-all-likes": "showAllLikes",
"click #show-all-reshares": "showAllReshares"
},
initialize: function() {
this.model.interactions.on("change", this.render, this);
this.model.interactions.likes.on("change", this.render, this);
this.model.interactions.reshares.on("change", this.render, this);
},
presenter: function() {
@ -15,8 +22,28 @@ app.views.SinglePostInteractionCounts = app.views.Base.extend({
reshares: interactions.reshares.toJSON(),
commentsCount: interactions.commentsCount(),
likesCount: interactions.likesCount(),
resharesCount: interactions.resharesCount()
resharesCount: interactions.resharesCount(),
showMoreLikes: interactions.likes.length < interactions.likesCount(),
showMoreReshares: interactions.reshares.length < interactions.resharesCount()
};
},
_showAll: function(interactionType, models) {
this.$("#show-all-" + interactionType).addClass("hidden");
this.$("#" + interactionType + " .loader").removeClass("hidden");
models.fetch({success: function() {
models.trigger("change");
}});
},
showAllLikes: function(evt) {
evt.preventDefault();
this._showAll("likes", this.model.interactions.likes);
},
showAllReshares: function(evt) {
evt.preventDefault();
this._showAll("reshares", this.model.interactions.reshares);
}
});
// @license-end

View file

@ -9,7 +9,8 @@ Diaspora.MarkdownEditor.prototype = {
this.options = {
resize: "none",
onHidePreview: $.noop,
onPostPreview: $.noop
onPostPreview: $.noop,
onChange: function(e) { autosize.update(e.$textarea); }
};
$.extend(this.options, opts);
@ -130,6 +131,14 @@ Diaspora.MarkdownEditor.prototype = {
}
},
isPreviewMode: function() {
return this.instance !== undefined && this.instance.$editor.find(".md-preview").length > 0;
},
userInputEmpty: function() {
return this.instance === undefined || this.instance.getContent().length === 0;
},
localize: function() {
var locale = Diaspora.I18n.language;
@ -160,3 +169,7 @@ Diaspora.MarkdownEditor.prototype = {
return locale;
}
};
Diaspora.MarkdownEditor.simplePreview = function($mdInstance) {
return "<div class='preview-content'>" + app.helpers.textFormatter($mdInstance.getContent()) + "</div>";
};

View file

@ -1,4 +1,4 @@
//= require jquery2
//= require jquery3
//= require handlebars.runtime
//= require templates
//= require main

View file

@ -1,5 +1,5 @@
//= require emojione
//= require favico.js
//= require favico.js/favico
//= require jquery.ui/ui/resizable
//= require jquery.ui/ui/draggable

View file

@ -10,10 +10,10 @@
//= require charcount
//= require jquery-placeholder
//= require jquery.timeago
//= require jquery-ui/core
//= require jquery-ui/widget
//= require jquery-ui/mouse
//= require jquery-ui/sortable
//= require jquery.ui/ui/core
//= require jquery.ui/ui/widget
//= require jquery.ui/ui/mouse
//= require jquery.ui/ui/sortable
//= require keycodes
//= require jquery.autoSuggest.custom
//= require fine-uploader/fine-uploader.core

View file

@ -1,9 +1,9 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
* licensed under the Affero General Public License version 3 or later. See
* the COPYRIGHT file.
*/
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
//= require jquery-textchange
//= require charcount
//= require js-routes
@ -18,6 +18,8 @@
//= require helpers/i18n
//= require helpers/profile_photo_uploader
//= require helpers/tags_autocomplete
//= require bootstrap-markdown/bootstrap-markdown
//= require helpers/markdown_editor
//= require widgets/timeago
//= require mobile/mobile_application
//= require mobile/mobile_file_uploader

View file

@ -11,6 +11,8 @@
initialize: function() {
var self = this;
new Diaspora.MarkdownEditor(".comment_box");
this.stream().on("tap click", "a.show-comments", function(evt){
evt.preventDefault();
self.toggleComments($(this));
@ -35,7 +37,7 @@
self.scrollToOffset(commentContainer);
});
this.stream().on("submit", ".new_comment", this.submitComment);
this.stream().on("submit", ".new-comment", this.submitComment);
},
submitComment: function(evt){

View file

@ -1,6 +1,7 @@
(function() {
Diaspora.Mobile.Conversations = {
initialize: function() {
new Diaspora.MarkdownEditor(".conversation-message-text");
if (Diaspora.Page !== "ConversationsNew") { return; }
$(document).on("ajax:success", "form#new-conversation", this.conversationCreateSuccess);
$(document).on("ajax:error", "form#new-conversation", this.conversationCreateError);

View file

@ -33,7 +33,6 @@ function createUploader(){
$("#fileInfo-publisher").text(fileName + " " + progress + "%");
},
onSubmit: function() {
$("#file-upload-publisher").addClass("loading");
$("#publisher_textarea_wrapper").addClass("with_attachments");
$("#photodropzone").append(
"<li class='publisher_photo loading' style='position:relative;'>" +

View file

@ -75,7 +75,7 @@
onLike: function(evt){
evt.preventDefault();
var link = $(evt.target),
var link = $(evt.target).closest(".like-action"),
likeCounter = $(evt.target).closest(".stream-element").find(".like-count");
if(!link.hasClass("loading") && link.hasClass("inactive")) {
@ -89,7 +89,7 @@
onReshare: function(evt) {
evt.preventDefault();
var link = $(this),
var link = $(this).closest(".reshare-action"),
href = link.attr("href"),
confirmText = link.attr("title");
@ -103,6 +103,10 @@
},
success: function() {
Diaspora.Mobile.PostActions.toggleActive(link);
var reshareCounter = $(evt.target).closest(".stream-element").find(".reshare-count");
if (reshareCounter) {
reshareCounter.text(parseInt(reshareCounter.text(), 10) + 1);
}
},
error: function(response) {
Diaspora.Mobile.Alert.handleAjaxError(response);

View file

@ -46,4 +46,6 @@ $(document).ready(function(){
evt.preventDefault();
$("#new_status_message").submit();
});
new Diaspora.MarkdownEditor("#status_message_text");
});

View file

@ -9,7 +9,6 @@
@import 'media-box';
@import 'entypo';
@import 'icons';
@import 'mentions';
@import 'animations';
@import 'flash_messages';
@import 'sprites';

View file

@ -71,7 +71,7 @@ pre { word-wrap: break-word; }
line-height: 50px;
opacity: 0;
position: fixed;
right: 54px;
right: 20px;
transition: opacity ease 400ms;
width: 50px;
z-index: 49;

View file

@ -1,7 +1,7 @@
// Calling this file bootstrap would cause an infinite recursion during asset compilation.
@import "bootstrap-sprockets";
@import "bootstrap-variables"; //our overwrites of bootstrap variables
@import "bootstrap";
@import "_bootstrap";
// Plugins

View file

@ -1,24 +1,20 @@
// Only overriding existing selectors here, so disable some lint rules
// scss-lint:disable IdSelector, SelectorFormat, NestingDepth, SelectorDepth, QualifyingElement
body {
.navbar.navbar-fixed-top #user_menu .dropdown-menu > li > a {
color: $text-color;
&:hover { color: $white; }
}
.publisher {
.mentions-input-box { background-color: $gray; }
form {
#publisher_textarea_wrapper { background-color: $gray; }
.btn.btn-link.question_mark:hover .entypo-cog { color: $gray-light; }
}
.write-preview-tabs > li.active * { color: $text-color; }
.md-preview { background-color: $gray; }
.md-cancel:hover .entypo-cross { color: $gray-light; }
.publisher-buttonbar .btn.btn-link:hover i { color: $gray-light; }
}
.write-preview-tabs > li.active * { color: $text-color; }
.md-cancel:hover .entypo-cross { color: $gray-light; }
.md-input,
.md-preview { background-color: $gray; }
.aspect_dropdown li a .text { color: $dropdown-link-color; }
.info .tag { background-color: $gray-light; }
@ -96,6 +92,7 @@ body {
#welcome-to-diaspora { background: $orange; }
.md-editor,
.block-form fieldset .form-control:focus { border-color: $input-border; }
&.page-registrations.action-new,

View file

@ -59,7 +59,8 @@ $dropdown-link-hover-color: $dropdown-link-color;
//== Navbar
$navbar-inverse-bg: $gray-darker;
$navbar-inverse-link-hover-color: $text-color;
$navbar-inverse-link-color: $gray-lighter;
$navbar-inverse-link-hover-color: $white;
$navbar-inverse-brand-hover-color: $navbar-inverse-link-hover-color;
//== Tabs

View file

@ -46,13 +46,12 @@
.comment.new-comment-form-wrapper { padding-bottom: 0; }
.submit_button {
.submit-button {
margin-top: 10px;
input {
float: right;
}
padding-left: 12px;
display: none;
}
.comment_box {
height: 35px;
@ -60,8 +59,27 @@
}
textarea.comment_box:focus, textarea.comment_box:valid, textarea.comment_box:active {
border-color: $border-dark-grey;
& + .submit_button { display: block; }
min-height: 35px;
box-shadow: none;
}
.twitter-typeahead {
// typeahead sets this to `display: inline-block` which adds additional space between
// the comment textarea and the submit button.
// scss-lint:disable ImportantRule
display: block !important;
// scss-lint:enable ImportantRule
}
}
.new-comment {
&:not(.open) .submit-button,
&:not(.open) .md-header {
display: none;
}
// The rule for .md-preview is required until we switch to the newer release of bootstrap-markdown with
// the following commit in:
// https://github.com/toopay/bootstrap-markdown/commit/14a21c3837140144b27efc19c795d1a37fad70fb
.md-preview,
&.open .md-editor textarea { min-height: 70px; }
}

View file

@ -238,3 +238,9 @@
}
.new-conversation.form-horizontal .form-group:last-of-type { margin-bottom: 0; }
// This rule is required until we switch to the newer release of bootstrap-markdown with
// the following commit in: https://github.com/toopay/bootstrap-markdown/commit/14a21c3837140144b27efc19c795d1a37fad70fb
.conversations-form-container .md-preview {
min-height: 105px;
}

View file

@ -1,68 +1,68 @@
.not-connected-menu {
.navbar-left {
float: left;
}
.navbar-right,
.navbar-right li {
float: right;
}
}
.navbar.navbar-fixed-top {
border-bottom: none;
box-shadow: 1px 0 2px $black;
a:focus {outline: 0 none; }
a:focus { outline: 0 none; }
.in {
overflow-y: visible; // Avoid search result dropdown to be hidden
}
.navbar-brand {
font-weight: bold;
font-size: $font-size-h3;
margin-left: -15px;
}
@media (max-width: $grid-float-breakpoint-max) {
.navbar-header > .nav li { display: inline-block !important; }
.nav-badge {
color: $navbar-inverse-link-color;
padding-left: 12px;
padding-right: 12px;
&:hover { color: $navbar-inverse-link-hover-color; }
&:hover,
&:focus {
background-color: transparent;
}
.header-title {
margin: (($navbar-height - 32px - 2 * $navbar-padding-vertical) / 2) 0;
img {
height: 32px;
opacity: .7;
}
#navbar-collapse {
.form-group, .twitter-typeahead {
display: block !important;
margin-bottom: 0;
&, & input { width: 100% }
}
img:hover {
opacity: 1;
}
}
@media (min-width: $grid-float-breakpoint) {
input[type="search"] {
@include transition(width);
margin-top: 2px;
width: 200px;
&:not(.active) {
background-color: $navbar-inverse-bg;
border-color: $gray-light;
width: 150px;
}
}
#user_menu {
&.open .dropdown-toggle { background-color: darken($navbar-inverse-bg, 7%); }
.dropdown-toggle {
margin: 0 1px;
min-width: 160px;
}
.dropdown-menu {
background-color: darken($navbar-inverse-bg, 7%);
border-top: none;
width: 100%;
& > li > a {
color: $gray-light;
padding-left: 55px;
&:hover {
background-color: $brand-primary;
color: $gray-lighter;
}
}
}
.navbar-header > ul,
.navbar-header > ul li {
float: left;
}
[class^="entypo-"],
[class*="entypo-"] {
color: inherit;
font-size: $font-size-h3;
vertical-align: middle;
}
.nav-badge {
padding: $navbar-padding-vertical 12px;
.badge {
position: absolute;
right: 10px;
top: 10px;
}
}
.navbar-nav:not(.nav-badges) > li > a { font-weight: bold; }
.nav-badges {
margin: 0;
li { height: $navbar-height; }
.dropdown-open {
background-color: $dropdown-bg;
@ -118,31 +118,141 @@
}
}
}
[class^="entypo-"], [class*="entypo-"] {
color: inherit;
font-size: $font-size-h3;
vertical-align: middle;
}
.nav-badge {
margin-bottom: -2px;
.badge {
position: absolute;
right: 10px;
top: 10px;
}
.user-menu-dropdown {
padding: 0;
}
#user_menu {
.user-avatar {
height: $navbar-height;
margin-bottom: -$navbar-padding-vertical;
margin-right: 10px;
margin-top: -$navbar-padding-vertical;
padding: ($navbar-height - 30px) / 2 0;
.avatar {
height: 30px;
width: 30px;
}
.user-avatar {
height: $navbar-height;
padding: ($navbar-height - 30px)/2 0;
margin-bottom: -$navbar-padding-vertical;
margin-top: -$navbar-padding-vertical;
margin-right: 10px;
}
.navbar-form .form-control { display: inline-block; }
@media (max-width: $grid-float-breakpoint-max) {
.navbar-nav.hidden-xs {
margin: 0;
a {
padding-bottom: $navbar-padding-vertical;
padding-top: $navbar-padding-vertical;
}
}
.nav-badge {
color: $navbar-inverse-link-color;
&:hover { color: $navbar-inverse-link-hover-color; }
&:hover,
&:focus {
background-color: transparent;
}
}
.form-group,
.twitter-typeahead {
margin-bottom: 0;
&,
input { width: 100%; }
}
.nav-badges .dropdown-menu {
width: 300px;
}
.navbar-collapse {
padding-top: $navbar-padding-vertical / 2;
.navbar-nav {
margin-bottom: 0;
margin-top: 0;
}
}
.user-menu-dropdown {
background-color: transparent;
border: 0;
box-shadow: none;
display: block;
padding: 0;
position: static;
width: 100%;
a {
color: $navbar-inverse-link-color;
font-weight: bold;
padding: $nav-link-padding;
&:hover {
background-color: $navbar-inverse-link-hover-bg;
color: $navbar-inverse-link-hover-color;
}
}
}
}
@media (min-width: $grid-float-breakpoint) {
.navbar-form { // set correct margin for small inputs
margin-bottom: ($navbar-height - $input-height-small) / 2;
margin-top: ($navbar-height - $input-height-small) / 2;
}
[type="search"] {
@include transition(width);
width: 200px;
&:not(.active) {
background-color: $navbar-inverse-bg;
border-color: $gray-light;
width: 150px;
}
}
.user-menu {
&.open .dropdown-toggle { background-color: darken($navbar-inverse-bg, 7%); }
.dropdown-toggle {
margin: 0 1px;
min-width: 160px;
}
}
.user-menu-dropdown {
background-color: darken($navbar-inverse-bg, 7%);
border-top: 0;
width: 100%;
a {
color: $navbar-inverse-link-color;
padding-left: 55px;
&:hover {
background-color: $list-group-hover-bg;
color: $list-group-link-hover-color;
}
}
li:last-child a {
padding-bottom: 6px;
}
}
}
@media (max-width: $screen-xs-max) {
.nav-badges .dropdown-menu {
position: fixed;
top: $navbar-height;
width: 100%;
}
}
}

View file

@ -1,3 +1,18 @@
.md-editor {
border: 1px solid $border-grey;
border-radius: $border-radius-small;
overflow: hidden;
&.active { border: 1px solid $border-dark-grey; }
textarea,
textarea:focus {
border: 0;
box-shadow: none;
margin: 0;
}
}
.md-footer,
.md-header {
background: $white;
@ -27,6 +42,7 @@
.btn-group {
margin-bottom: 8px;
&:first-child { margin-left: 0; }
[class^="entypo-"],
[class*="entypo-"] {
@ -77,13 +93,11 @@
.md-preview {
background: $white;
color: $text-color;
// !important is needed to override the CSS rules dynamically added to the element
// scss-lint:disable ImportantRule
height: auto !important;
// scss-lint:enable ImportantRule
min-height: 90px;
overflow: auto;
position: relative;
// !important is needed to override the CSS rules dynamically added to the element
@ -91,6 +105,8 @@
width: 100% !important;
// scss-lint:enable ImportantRule
z-index: 10;
.preview-content { padding: 10px; }
}
.md-controls {

View file

@ -1,91 +0,0 @@
.mentions-input-box {
border-radius: 3px;
background: #fff;
position: relative;
textarea {
display: block;
background: transparent;
border: 1px solid #dcdcdc;
border-radius: 3px;
outline: 0;
overflow: hidden;
position: relative;
resize: none;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.form-control[disabled] { background-color: transparent; }
.mentions-autocomplete-list {
background: white;
display: none;
left: 0;
margin-left: -1px;
position: absolute;
right: 0;
z-index: 10000;
ul {
border: 1px solid $border-dark-grey;
margin: 0;
padding: 0;
border-radius: 0px 0px 5px 5px;
li {
color: #444;
background: white;
border-bottom: 1px solid #ccc;
cursor: pointer;
font-size: 15px;
height: 26px;
line-height: 26px;
list-style: none;
margin: 0;
overflow: hidden;
padding: 5px;
white-space: nowrap;
&:hover, &.active { background: $background-grey; }
&:last-child { border-radius: 0px 0px 5px 5px; }
img, div.icon {
float: left;
height: 25px;
margin-right: 5px;
width: 25px;
}
}
}
}
.mentions-box {
position: absolute;
right: 0px;
bottom: 0px;
left: 0px;
top: 0px;
padding: $padding-base-vertical $padding-base-horizontal;
}
.mentions {
color: transparent;
font-size: $font-size-base;
font-family: Arial, Helvetica, sans-serif;
overflow: hidden;
width: 100%;
white-space: pre-wrap;
word-wrap: break-word;
> strong {
background: $background-blue;
font-weight: normal;
}
}
}

View file

@ -43,16 +43,15 @@
display: flex;
.count {
color: $text-color;
font-family: $font-family-base;
font-size: $font-size-base;
line-height: 22px;
margin-left: 5px;
vertical-align: top;
z-index: 2;
}
.icon-count-group {
display: flex;
margin: 0 7px;
}
[class^="entypo"] {
color: $text-grey;
font-size: 24px;
@ -73,6 +72,9 @@
}
.post-action {
display: flex;
margin: 0 7px;
.disabled { color: $medium-gray; }
}

View file

@ -33,10 +33,15 @@ $mobile-navbar-height: 46px;
li { float: left; }
}
#header-title {
.header-title {
padding: 7px 15px;
margin: 0 0 0 -15px;
height: $mobile-navbar-height;
img {
height: 30px;
width: 30px;
}
}
#nav-badges {
@ -87,12 +92,6 @@ $mobile-navbar-height: 46px;
background-color: $red;
}
}
#header-title{
img {
height: 30px;
width: 30px;
}
}
}
#drawer {

View file

@ -0,0 +1,6 @@
.md-editor {
border: 1px solid $light-grey;
border-radius: $btn-border-radius-base;
&.active { border-color: $text-grey; }
}

View file

@ -16,6 +16,8 @@
@import "mobile/stream_element";
@import "mobile/comments";
@import 'mobile/openid_connect_error_page';
@import 'markdown-editor';
@import 'mobile/markdown_editor';
@import 'typography';
@ -42,8 +44,6 @@ h3 { margin-top: 0; }
.clear { clear: both; }
#main { padding: 56px 10px 0 10px; }
textarea { resize: vertical; }
.avatar {
border-radius: 4px;
}
@ -55,7 +55,6 @@ textarea { resize: vertical; }
overflow: auto;
position: relative;
text-align: left;
min-height: 34px;
padding: 10px 0 0 0;
list-style: none;
@ -404,11 +403,6 @@ footer {
.counter {
font-size: 14px;
}
textarea {
min-width: 100%;
max-width: 100%;
}
}
select {
@ -539,13 +533,6 @@ form#new_conversation.new_conversation {
}
}
textarea#conversation_text {
font-size: larger;
width: 100%;
min-width: 100%;
max-width: 100%;
}
.registrations_error,
.registrations_notice {
color: #DF0101;
@ -714,33 +701,6 @@ select#aspect_ids_ {
position: relative;
padding-right: 10px;
#hide_publisher {
opacity: 0.3;
z-index: 5;
padding: 3px;
position: absolute;
right: 6px;
top: 0;
&:hover {
opacity: 1;
}
}
&.active {
border: 1px solid $border-dark-grey;
}
textarea {
z-index: 2;
border: none;
&:focus {
outline: 0;
background: transparent;
}
}
&.with_attachments {
padding-bottom: 55px;
border: 1px solid $border-medium-grey;

View file

@ -14,21 +14,11 @@
display: none !important;
}
.mentions-box {
margin-top: 0;
}
#publisher_textarea_wrapper { border: 1px solid $border-grey !important; }
}
.container-fluid{ padding: 0; }
.twitter-typeahead {
width: calc(100% + 2px);
.tt-menu { width: 100%; }
}
form {
margin: 0;
#fileInfo { display: none !important; }
@ -86,11 +76,10 @@
}
textarea {
background: transparent;
border: 0 solid $light-grey;
margin: 0;
box-shadow: none;
resize: none;
height: 50px;
resize: none;
}
&.active textarea {
@ -202,9 +191,27 @@
margin-bottom: 0;
}
}
.md-editor,
.md-editor.active {
border: 0;
}
.md-preview {
// This rule is required until we switch to the newer release of bootstrap-markdown with
// the following commit in:
// https://github.com/toopay/bootstrap-markdown/commit/14a21c3837140144b27efc19c795d1a37fad70fb
min-height: 90px;
.stream-element .post-content .markdown-content {
padding-top: 0;
}
}
}
.publisher-textarea-wrapper {
position: relative;
&:not(.with-location) .location-container,
&.markdown-preview .location-container,
&:not(.with-poll) .poll-creator-container,
@ -248,20 +255,12 @@
.locator { display: none; }
}
&.submitting .mentions-box { display: none; }
.twitter-typeahead {
left: -1px;
// Override inline rule of Typeahead
// scss-lint:disable ImportantRule
position: absolute !important;
// scss-lint:enable ImportantRule
}
.mentions-box {
// Leave space for markdown editor header
margin-top: 42px;
}
}
.publisher-buttonbar {

View file

@ -152,5 +152,16 @@
.interaction-avatars {
overflow: hidden;
.author-name:focus,
.author-name:hover {
text-decoration: none;
}
.loader {
height: $line-height-computed;
vertical-align: text-bottom;
width: $line-height-computed;
}
}
}

View file

@ -43,3 +43,21 @@
}
}
}
.typeahead-mention-box-wrap .twitter-typeahead {
left: -1px;
width: calc(100% + 2px);
.tt-menu {
// Override inline rule of Typeahead
// If this is not overridden (`position: absolute` by default in Typeahead) then
// the box is cut when opened because of the `overflow: hidden` from parent classes of comment form styles. By
// having `position: relative` here we make it visible by inserting it in the flow.
// This has a side effect of "Comment" button move down when box is open, but it feels like the least evil.
// scss-lint:disable ImportantRule
position: relative !important;
// scss-lint:enable ImportantRule
width: 100%;
}
}

View file

@ -25,9 +25,16 @@
{{/with}}
<div class="bd">
<form accept-charset="UTF-8" action="/posts/{{id}}/comments" class="new_comment" id="new_comment_on_{{id}}" method="post">
<textarea class="comment_box form-control" id="comment_text_on_{{id}}" name="text" rows="1" required placeholder="{{t "stream.comment"}}" />
<div class="submit_button">
<form accept-charset="UTF-8" action="/posts/{{id}}/comments"
class="new-comment" id="new-comment-on-{{id}}" method="post">
<textarea class="comment_box form-control mention-textarea"
id="comment_text_on_{{id}}" name="text" rows="1" required placeholder="{{t "stream.comment"}}" />
<div class="typeahead-mention-box-wrap">
<input class="typeahead-mention-box hidden" type="text">
</div>
<div class="submit-button">
<input class="btn btn-primary" id="comment_submit_{{id}}" name="commit" type="submit" value="{{t "stream.comment"}}" />
</div>
</form>

View file

@ -17,6 +17,7 @@
<li>{{{ get_support_a_irc }}}</li>
<li>{{{ get_support_a_faq }}}</li>
<li>{{{ get_support_a_hashtag }}}</li>
<li>{{{ get_support_a_discourse }}}</li>
</ul>
</div>
</div>

View file

@ -28,7 +28,7 @@
<a class='toggle' href='#'>
<h4>{{ post_report_q }}</h4>
</a>
<div class='answer hideable'>{{ post_report_a }}</div>
<div class='answer hideable'>{{{ post_report_a }}}</div>
</div>
<div class='question opened collapsible'>
<a class='toggle' href='#'>
@ -59,9 +59,7 @@
<h4>{{ insert_images_comments_q }}</h4>
</a>
<div class='answer hideable'>
{{ insert_images_comments_a1 }}
<pre>![{{ image_text }}]( {{ image_url }})</pre>
{{ insert_images_comments_a2 }}
{{ insert_images_comments_a }}
</div>
</div>
<div class='question opened collapsible'>

View file

@ -13,29 +13,17 @@
</span>
{{#if preview}}
<span>{{t "stream.like"}}</span>
{{else}}
<a href="#" class="like" rel='nofollow'>
{{~#if userLike~}}
{{~t "stream.unlike"~}}
{{~else~}}
{{~t "stream.like"~}}
{{~/if~}}
</a>
{{/if}}
<a href="#" class="like" rel='nofollow'>
{{~#if userLike~}}
{{~t "stream.unlike"~}}
{{~else~}}
{{~t "stream.like"~}}
{{~/if~}}
</a>
·
{{#if preview}}
<span>{{t "stream.reshare"}}</span>
·
{{else if userCanReshare}}
{{#if userCanReshare}}
<a href="#" class="reshare" rel='nofollow'>{{t "stream.reshare"}}</a>
·
{{/if}}
{{#if preview}}
<span>{{t "stream.comment"}}</span>
{{else}}
<a href="#" class="focus_comment_textarea" rel="nofollow">{{t "stream.comment"}}</a>
{{/if}}
<a href="#" class="focus_comment_textarea" rel="nofollow">{{t "stream.comment"}}</a>

View file

@ -10,46 +10,24 @@
<span class="icon-bar"></span>
</button>
<a href="/stream" class="navbar-brand" data-stream-title="{{t "my_stream"}}">
{{ podname }}
<span class="hidden-xs">{{ podname }}</span>
<div class="visible-xs-block header-title">
<img src="{{imageUrl 'branding/logos/asterisk_white_mobile.png'}}" alt="{{ podname }}" />
</div>
</a>
<ul class="nav nav-badges visible-sm">
<li>
<a href="/notifications" title="{{t "header.notifications"}}" class="notifications-link nav-badge">
<i class="entypo-bell"></i>
<span class="badge badge-important {{#unless current_user.notifications_count}} hidden {{/unless}}">
{{current_user.notifications_count}}
</span>
</a>
</li>
<li>
<a href="/conversations" title="{{t "header.conversations"}}" class="conversations-link nav-badge">
<i class="entypo-mail"></i>
<span class="badge badge-important {{#unless current_user.unread_messages_count}} hidden {{/unless}}">
{{current_user.unread_messages_count}}
</span>
</a>
</li>
</ul>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav navbar-left">
<ul class="nav navbar-nav hidden-xs">
<li><a href="/stream">{{t "my_stream"}}</a></li>
<li><a href="/activity">{{t "my_activity"}}</a></li>
<li class="visible-xs"><a href="/notifications">{{t "header.notifications"}}</a></li>
<li class="visible-xs"><a href="/conversations">{{t "header.conversations"}}</a></li>
<li class="visible-sm visible-xs"><a href="/mobile/toggle">{{t "header.toggle_mobile"}}</a></li>
</ul>
<ul class="nav navbar-nav navbar-left nav-badges hidden-sm hidden-xs">
<ul class="nav navbar-nav nav-badges">
<li class="dropdown" id="notification-dropdown">
<a id="notifications-link" href="/notifications" title="{{t "header.notifications"}}" class="notifications-link nav-badge hidden-sm hidden-xs" role="button" data-toggle="dropdown" aria-expanded="false" data-target=" ">
<a id="notifications-link" href="/notifications" title="{{t "header.notifications"}}" class="notifications-link nav-badge"
role="button" data-toggle="dropdown" aria-expanded="false" data-target=" ">
<i class="entypo-bell"></i>
<span class="badge badge-important {{#unless current_user.notifications_count}} hidden {{/unless}}">
{{current_user.notifications_count}}
</span>
</a>
<ul class="dropdown-menu" role="menu">
<div class="header">
<div class="pull-right">
@ -71,11 +49,9 @@
</a>
</div>
</ul>
</li>
<li>
<a id="conversations-link" href="/conversations" title="{{t "header.conversations"}}" class="conversations-link nav-badge hidden-sm hidden-xs">
<a id="conversations-link" href="/conversations" title="{{t "header.conversations"}}" class="conversations-link nav-badge">
<i class="entypo-mail"></i>
<span class="badge badge-important {{#unless current_user.unread_messages_count}} hidden {{/unless}}">
{{current_user.unread_messages_count}}
@ -83,10 +59,17 @@
</a>
</li>
</ul>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav navbar-left visible-sm-block visible-xs-block">
<li class="visible-xs-block"><a href="/stream">{{t "my_stream"}}</a></li>
<li class="visible-xs-block"><a href="/activity">{{t "my_activity"}}</a></li>
<li><a href="/mobile/toggle">{{t "header.toggle_mobile"}}</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown" id="user_menu">
<a href="{{urlTo "person" current_user.guid}}" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
<li class="dropdown user-menu" id="user-menu">
<a href="{{urlTo "person" current_user.guid}}" class="dropdown-toggle hidden-xs hidden-sm" data-toggle="dropdown" role="button" aria-expanded="false">
<span class="user-avatar pull-left">
{{{personImage current_user "small"}}}
</span>
@ -94,7 +77,7 @@
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<ul class="dropdown-menu user-menu-dropdown" role="menu">
<li><a href="/people/{{current_user.guid}}">{{t "header.profile"}}</a></li>
<li><a href="/contacts">{{t "header.contacts"}}</a></li>
<li><a href="/user/edit">{{t "header.settings"}}</a></li>

View file

@ -36,22 +36,18 @@
<a href="#" class="section-unselected faq-link" data-section="private_posts" data-items="who_sees_post can_comment can_reshare see_comment">{{ title_private_posts }}</a>
<span class="section-selected">{{ title_private_posts }}</span>
</li>
<li>
<a href="#" class="section-unselected faq-link" data-section="private_profiles" data-items="who_sees_profile whats_in_profile who_sees_updates">{{ title_private_profiles }}</a>
<span class="section-selected">{{ title_private_profiles }}</span>
</li>
<li>
<a href="#" class="section-unselected faq-link" data-section="public_posts" data-items="who_sees_post find_public_post can_comment_reshare_like see_comment_reshare_like deselect_aspect_posting">{{ title_public_posts }}</a>
<span class="section-selected">{{ title_public_posts }}</span>
</li>
<li>
<a href="#" class="section-unselected faq-link" data-section="public_profiles" data-items="who_sees_profile whats_in_profile who_sees_updates what_do_tags_do">{{ title_public_profiles }}</a>
<span class="section-selected">{{ title_public_profiles }}</span>
</li>
<li>
<a href="#" class="section-unselected faq-link" data-section="resharing_posts" data-items="reshare_public_post_aspects reshare_private_post_aspects">{{ title_resharing_posts }}</a>
<span class="section-selected">{{ title_resharing_posts }}</span>
</li>
<li>
<a href="#" class="section-unselected faq-link" data-section="profile" data-items="whats_in_profile who_sees_profile what_do_tags_do">{{ title_profile }}</a>
<span class="section-selected">{{ title_profile }}</span>
</li>
<li>
<a href="#" class="section-unselected faq-link-sharing" data-section="sharing" data-items="">{{ title_sharing }}</a>
<span class="section-selected">{{ title_sharing }}</span>

View file

@ -10,6 +10,12 @@
{{{personImage this "small" "micro"}}}
{{/linkToAuthor}}
{{/each}}
{{#if showMoreReshares}}
<div class="loader hidden">
<div class="spinner"></div>
</div>
<div id="show-all-reshares" class="btn btn-sm btn-link">{{t "show_all"}}</div>
{{/if}}
</div>
</div>
{{/if}}
@ -25,6 +31,12 @@
{{{personImage this "small" "micro"}}}
{{/linkToAuthor}}
{{/each}}
{{#if showMoreLikes}}
<div class="loader hidden">
<div class="spinner"></div>
</div>
<div id="show-all-likes" class="btn btn-sm btn-link">{{t "show_all"}}</div>
{{/if}}
</div>
</div>
{{/if}}

View file

@ -1,25 +1,26 @@
<div class="media {{#if showPost}} {{#if nsfw}} shield-off {{/if}} {{else}} shield-active {{/if}}">
{{#with author}}
<a href="/people/{{guid}}" class="img {{{hovercardable this}}}">
{{{personImage this}}}
</a>
{{/with}}
{{#unless preview}}
{{#with author}}
<a href="/people/{{guid}}" class="img {{{hovercardable this}}}">
{{{personImage this}}}
</a>
{{/with}}
{{/unless}}
<div class="bd">
{{#if loggedIn}}
<div class="post-controls"></div>
{{/if}}
{{#unless preview}}
{{#if loggedIn}}
<div class="post-controls"></div>
{{/if}}
<div>
{{#linkToAuthor author}}
{{~name~}}
{{/linkToAuthor}}
<div>
{{#linkToAuthor author}}
{{~name~}}
{{/linkToAuthor}}
<span class="details gray">
-
{{#if preview}}
<time class="timeago" data-original-title="{{{localTime created_at}}}" datetime="{{created_at}}" />
{{else}}
<span class="details gray">
-
<a href="/posts/{{id}}">
<time class="timeago" data-original-title="{{{localTime created_at}}}" datetime="{{created_at}}" />
</a>
@ -27,16 +28,18 @@
<a href="/posts/{{guid}}" class="permalink" title="{{t "stream.permalink"}}">
<i class="entypo-link"></i>
</a>
{{/if}}
</span>
</div>
</span>
</div>
{{/unless}}
<div class="post-content"> </div>
<div class="status-message-location nsfw-hidden"> </div>
<div class="feedback nsfw-hidden"> </div>
<div class="likes nsfw-hidden"> </div>
<div class="reshares nsfw-hidden"> </div>
<div class="comments nsfw-hidden"> </div>
{{#unless preview}}
<div class="feedback nsfw-hidden"> </div>
<div class="likes nsfw-hidden"> </div>
<div class="reshares nsfw-hidden"> </div>
<div class="comments nsfw-hidden"> </div>
{{/unless}}
</div>
</div>

View file

@ -1,8 +1,6 @@
module Admin
class AdminController < ApplicationController
before_filter :authenticate_user!
before_filter :redirect_unless_admin
before_action :authenticate_user!
before_action :redirect_unless_admin
end
end

View file

@ -205,7 +205,7 @@ module Api
if prompt && prompt.include?("none")
handle_prompt_none
elsif prompt && prompt.include?("login")
new_params = params.except("controller", "action").merge(prompt: prompt.remove("login"))
new_params = params.except("controller", "action").permit!.to_h.merge(prompt: prompt.remove("login"))
reauthenticate(new_params)
else
authenticate_user!

View file

@ -24,17 +24,6 @@
module Api
module OpenidConnect
class DiscoveryController < ApplicationController
def webfinger
jrd = {
links: [{
rel: OpenIDConnect::Discovery::Provider::Issuer::REL_VALUE,
href: root_url
}]
}
jrd[:subject] = params[:resource] if params[:resource].present?
render json: jrd, content_type: "application/jrd+json"
end
def configuration
render json: OpenIDConnect::Discovery::Provider::Config::Response.new(
issuer: root_url,

View file

@ -8,7 +8,8 @@ module Api
if req["client_assertion_type"] == "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
handle_jwt_bearer(req)
end
self.status, response.headers, self.response_body = Api::OpenidConnect::TokenEndpoint.new.call(request.env)
self.status, headers, self.response_body = Api::OpenidConnect::TokenEndpoint.new.call(request.env)
headers.each {|name, value| response.headers[name] = value }
nil
end

View file

@ -5,7 +5,7 @@
class ApplicationController < ActionController::Base
before_action :force_tablet_html
has_mobile_fu
protect_from_forgery except: :receive, with: :exception
protect_from_forgery except: :receive, with: :exception, prepend: true
rescue_from ActionController::InvalidAuthenticityToken do
if user_signed_in?

View file

@ -6,7 +6,7 @@
class AspectMembershipsController < ApplicationController
before_action :authenticate_user!
respond_to :html, :json
respond_to :json
def destroy
aspect = current_user.aspects.joins(:aspect_memberships).where(aspect_memberships: {id: params[:id]}).first
@ -30,18 +30,9 @@ class AspectMembershipsController < ApplicationController
if success
render json: AspectMembershipPresenter.new(membership).base_hash
else
render text: membership.errors.full_messages, status: 403
render plain: membership.errors.full_messages, status: 403
end
end
format.all do
if success
flash.now[:notice] = I18n.t "aspect_memberships.destroy.success"
else
flash.now[:error] = I18n.t "aspect_memberships.destroy.failure"
end
redirect_to :back
end
end
end
@ -58,35 +49,25 @@ class AspectMembershipsController < ApplicationController
AspectMembership.where(contact_id: @contact.id, aspect_id: @aspect.id).first)
.base_hash
end
format.all do
flash.now[:notice] = I18n.t("aspects.add_to_aspect.success")
redirect_to :back
end
end
else
respond_to do |format|
format.json do
render text: I18n.t("aspects.add_to_aspect.failure"), status: 409
end
format.all do
flash.now[:error] = I18n.t("aspects.add_to_aspect.failure")
render nothing: true, status: 409
render plain: I18n.t("aspects.add_to_aspect.failure"), status: 409
end
end
end
end
rescue_from ActiveRecord::StatementInvalid do
render text: I18n.t("aspect_memberships.destroy.invalid_statement"), status: 400
render plain: I18n.t("aspect_memberships.destroy.invalid_statement"), status: 400
end
rescue_from ActiveRecord::RecordNotFound do
render text: I18n.t("aspect_memberships.destroy.no_membership"), status: 404
render plain: I18n.t("aspect_memberships.destroy.no_membership"), status: 404
end
rescue_from Diaspora::NotMine do
render text: I18n.t("aspect_memberships.destroy.forbidden"), status: 403
render plain: I18n.t("aspect_memberships.destroy.forbidden"), status: 403
end
end

View file

@ -22,7 +22,7 @@ class AspectsController < ApplicationController
render json: result
else
render nothing: true, status: 422
head :unprocessable_entity
end
end
@ -71,7 +71,7 @@ class AspectsController < ApplicationController
params[:ordered_aspect_ids].each_with_index do |id, i|
current_user.aspects.find(id).update_attributes(order_id: i)
end
render nothing: true
head :no_content
end
def toggle_chat_privilege
@ -79,7 +79,7 @@ class AspectsController < ApplicationController
@aspect.chat_enabled = !@aspect.chat_enabled
@aspect.save
render :nothing => true
head :no_content
end
def toggle_contact_visibility
@ -91,7 +91,7 @@ class AspectsController < ApplicationController
@aspect.contacts_visible = true
end
@aspect.save
render :nothing => true
head :no_content
end
private

View file

@ -1,34 +1,26 @@
class BlocksController < ApplicationController
before_action :authenticate_user!
respond_to :html, :json
def create
block = current_user.blocks.new(block_params)
if block.save
disconnect_if_contact(block.person)
notice = {:notice => t('blocks.create.success')}
else
notice = {:error => t('blocks.create.failure')}
end
disconnect_if_contact(block.person) if block.save
respond_with do |format|
format.html{ redirect_to :back, notice }
format.json{ render :nothing => true, :status => 204 }
respond_to do |format|
format.json { head :no_content }
end
end
def destroy
if current_user.blocks.find(params[:id]).delete
notice = {:notice => t('blocks.destroy.success')}
else
notice = {:error => t('blocks.destroy.failure')}
end
notice = if current_user.blocks.find_by(id: params[:id])&.delete
{notice: t("blocks.destroy.success")}
else
{error: t("blocks.destroy.failure")}
end
respond_with do |format|
format.html{ redirect_to :back, notice }
format.json{ render :nothing => true, :status => 204 }
respond_to do |format|
format.json { head :no_content }
format.any { redirect_back fallback_location: privacy_settings_path, flash: notice }
end
end

View file

@ -8,21 +8,21 @@ class CommentsController < ApplicationController
respond_to :html, :mobile, :json
rescue_from ActiveRecord::RecordNotFound do
render nothing: true, status: 404
head :not_found
end
def create
begin
comment = comment_service.create(params[:post_id], params[:text])
rescue ActiveRecord::RecordNotFound
render text: I18n.t("comments.create.error"), status: 404
render plain: I18n.t("comments.create.error"), status: 404
return
end
if comment
respond_create_success(comment)
else
render text: I18n.t("comments.create.error"), status: 422
render plain: I18n.t("comments.create.error"), status: 422
end
end
@ -57,24 +57,24 @@ class CommentsController < ApplicationController
def respond_create_success(comment)
respond_to do |format|
format.json { render json: CommentPresenter.new(comment), status: 201 }
format.html { render nothing: true, status: 201 }
format.html { head :created }
format.mobile { render partial: "comment", locals: {comment: comment} }
end
end
def respond_destroy_success
respond_to do |format|
format.mobile { redirect_to :back }
format.js { render nothing: true, status: 204 }
format.json { render nothing: true, status: 204 }
format.mobile { redirect_back fallback_location: stream_path }
format.js { head :no_content }
format.json { head :no_content }
end
end
def respond_destroy_error
respond_to do |format|
format.mobile { redirect_to :back }
format.js { render nothing: true, status: 403 }
format.json { render nothing: true, status: 403 }
format.mobile { redirect_back fallback_location: stream_path }
format.js { head :forbidden }
format.json { head :forbidden }
end
end
end

View file

@ -39,7 +39,7 @@ class ConversationsController < ApplicationController
end
unless person_ids.present?
render text: I18n.t("javascripts.conversation.create.no_recipient"), status: 422
render plain: I18n.t("javascripts.conversation.create.no_recipient"), status: 422
return
end
@ -53,7 +53,7 @@ class ConversationsController < ApplicationController
flash[:notice] = I18n.t("conversations.create.sent")
render json: {id: @conversation.id}
else
render text: I18n.t("conversations.create.fail"), status: 422
render plain: I18n.t("conversations.create.fail"), status: 422
end
end
@ -82,7 +82,7 @@ class ConversationsController < ApplicationController
@conversation.set_read(current_user)
render partial: "conversations/show", locals: {conversation: @conversation}
else
render nothing: true, status: 404
head :not_found
end
end

View file

@ -33,7 +33,7 @@ class HomeController < ApplicationController
def toggle_mobile
session[:mobile_view] = session[:mobile_view].nil? ? true : !session[:mobile_view]
redirect_to :back
redirect_back fallback_location: root_path
end
def force_mobile

View file

@ -42,7 +42,7 @@ class InvitationsController < ApplicationController
t("invitations.create.rejected", emails: invalid_emails.join(", "))
end
redirect_to :back
redirect_back fallback_location: stream_path
end
private
@ -55,7 +55,7 @@ class InvitationsController < ApplicationController
else
t("invitations.create.closed")
end
redirect_to :back
redirect_back fallback_location: stream_path
end
def valid_email?(email)
@ -71,6 +71,6 @@ class InvitationsController < ApplicationController
end
def inviter_params
params.require(:email_inviter).permit(:message, :locale, :emails)
params.require(:email_inviter).permit(:message, :locale, :emails).to_h
end
end

View file

@ -13,10 +13,10 @@ class LikesController < ApplicationController
def create
like = like_service.create(params[:post_id])
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
render text: I18n.t("likes.create.error"), status: 422
render plain: I18n.t("likes.create.error"), status: 422
else
respond_to do |format|
format.html { render nothing: true, status: 201 }
format.html { head :created }
format.mobile { redirect_to post_path(like.post_id) }
format.json { render json: like.as_api_response(:backbone), status: 201 }
end
@ -24,9 +24,9 @@ class LikesController < ApplicationController
def destroy
if like_service.destroy(params[:id])
render nothing: true, status: 204
head :no_content
else
render text: I18n.t("likes.destroy.error"), status: 404
render plain: I18n.t("likes.destroy.error"), status: 404
end
end

View file

@ -18,14 +18,6 @@ class MessagesController < ApplicationController
logger.info "event=create type=message user=#{current_user.diaspora_handle} status=success " \
"message=#{message.id} chars=#{params[:message][:text].length}"
Diaspora::Federation::Dispatcher.defer_dispatch(current_user, message)
# TODO: can be removed when messages are not relayed anymore
conversation_owner = conversation.author.owner
if conversation_owner && conversation_owner != current_user
remote_subs = conversation.participants.remote.ids
opts = {subscriber_ids: remote_subs}
Diaspora::Federation::Dispatcher.defer_dispatch(conversation_owner, message, opts) unless remote_subs.empty?
end
else
flash[:error] = I18n.t('conversations.new_conversation.fail')
end

View file

@ -1,7 +1,4 @@
class NodeInfoController < ApplicationController
respond_to :json
respond_to :html, only: :statistics
def jrd
render json: NodeInfo.jrd(CGI.unescape(node_info_url("123.123").sub("123.123", "%{version}")))
end
@ -17,7 +14,7 @@ class NodeInfoController < ApplicationController
def statistics
respond_to do |format|
format.json { render json: StatisticsPresenter.new }
format.json { head :not_acceptable }
format.all { @statistics = NodeInfoPresenter.new("1.0") }
end
end

View file

@ -23,8 +23,8 @@ class NotificationsController < ApplicationController
def index
conditions = {:recipient_id => current_user.id}
if params[:type] && Notification.types.has_key?(params[:type])
conditions[:type] = Notification.types[params[:type]]
if params[:type] && types.has_key?(params[:type])
conditions[:type] = types[params[:type]]
end
if params[:show] == "unread" then conditions[:unread] = true end
page = params[:page] || 1
@ -44,7 +44,7 @@ class NotificationsController < ApplicationController
@grouped_unread_notification_counts = {}
Notification.types.each_with_object(current_user.unread_notifications.group_by(&:type)) {|(name, type), notifications|
types.each_with_object(current_user.unread_notifications.group_by(&:type)) {|(name, type), notifications|
@grouped_unread_notification_counts[name] = notifications.has_key?(type) ? notifications[type].count : 0
}
@ -65,7 +65,7 @@ class NotificationsController < ApplicationController
end
def read_all
current_type = Notification.types[params[:type]]
current_type = types[params[:type]]
notifications = Notification.where(recipient_id: current_user.id, unread: true)
notifications = notifications.where(type: current_type) if params[:type]
notifications.update_all(unread: false)
@ -93,4 +93,17 @@ class NotificationsController < ApplicationController
}
}.as_json
end
def types
{
"also_commented" => "Notifications::AlsoCommented",
"comment_on_post" => "Notifications::CommentOnPost",
"liked" => "Notifications::Liked",
"mentioned" => "Notifications::MentionedInPost",
"mentioned_in_comment" => "Notifications::MentionedInComment",
"reshared" => "Notifications::Reshared",
"started_sharing" => "Notifications::StartedSharing"
}
end
helper_method :types
end

View file

@ -5,9 +5,9 @@ class ParticipationsController < ApplicationController
post = current_user.find_visible_shareable_by_id(Post, params[:post_id])
if post
current_user.participate! post
render nothing: true, status: :created
head :created
else
render nothing: true, status: :forbidden
head :forbidden
end
end
@ -15,9 +15,9 @@ class ParticipationsController < ApplicationController
participation = current_user.participations.find_by target_id: params[:post_id]
if participation
participation.destroy
render nothing: true, status: :ok
head :ok
else
render nothing: true, status: :unprocessable_entity
head :unprocessable_entity
end
end
end

View file

@ -19,8 +19,8 @@ class PeopleController < ApplicationController
rescue_from Diaspora::AccountClosed do
respond_to do |format|
format.any { redirect_to :back, :notice => t("people.show.closed_account") }
format.json { render :nothing => true, :status => 410 } # 410 GONE
format.any { redirect_back fallback_location: root_path, notice: t("people.show.closed_account") }
format.json { head :gone }
end
end
@ -115,15 +115,15 @@ class PeopleController < ApplicationController
def retrieve_remote
if params[:diaspora_handle]
Workers::FetchWebfinger.perform_async(params[:diaspora_handle])
render :nothing => true
head :ok
else
render :nothing => true, :status => 422
head :unprocessable_entity
end
end
def contacts
respond_to do |format|
format.json { render nothing: true, status: 406 }
format.json { head :not_acceptable }
format.any do
@person = Person.find_by_guid(params[:person_id])

View file

@ -66,10 +66,10 @@ class PhotosController < ApplicationController
:status => 201}
end
else
render :nothing => true, :status => 422
head :unprocessable_entity
end
else
render :nothing => true, :status => 422
head :unprocessable_entity
end
end
@ -80,7 +80,7 @@ class PhotosController < ApplicationController
current_user.retract(photo)
respond_to do |format|
format.json{ render :nothing => true, :status => 204 }
format.json { head :no_content }
format.html do
flash[:notice] = I18n.t 'photos.destroy.notice'
if StatusMessage.find_by_guid(photo.status_message_guid)
@ -125,27 +125,28 @@ class PhotosController < ApplicationController
end
def legacy_create
if params[:photo][:aspect_ids] == "all"
params[:photo][:aspect_ids] = current_user.aspects.collect { |x| x.id }
elsif params[:photo][:aspect_ids].is_a?(Hash)
params[:photo][:aspect_ids] = params[:photo][:aspect_ids].values
photo_params = params.require(:photo).permit(:pending, :set_profile_photo, aspect_ids: [])
if photo_params[:aspect_ids] == "all"
photo_params[:aspect_ids] = current_user.aspects.map(&:id)
elsif photo_params[:aspect_ids].is_a?(Hash)
photo_params[:aspect_ids] = params[:photo][:aspect_ids].values
end
params[:photo][:user_file] = file_handler(params)
photo_params[:user_file] = file_handler(params)
@photo = current_user.build_post(:photo, params[:photo])
@photo = current_user.build_post(:photo, photo_params)
if @photo.save
unless @photo.pending
unless @photo.public?
aspects = current_user.aspects_from_ids(params[:photo][:aspect_ids])
aspects = current_user.aspects_from_ids(photo_params[:aspect_ids])
current_user.add_to_streams(@photo, aspects)
end
current_user.dispatch_post(@photo, :to => params[:photo][:aspect_ids])
current_user.dispatch_post(@photo, to: photo_params[:aspect_ids])
end
if params[:photo][:set_profile_photo]
if photo_params[:set_profile_photo]
profile_params = {:image_url => @photo.url(:thumb_large),
:image_url_medium => @photo.url(:thumb_medium),
:image_url_small => @photo.url(:thumb_small)}

View file

@ -1,20 +1,17 @@
class PollParticipationsController < ApplicationController
include ApplicationHelper
before_filter :authenticate_user!
before_action :authenticate_user!
def create
answer = PollAnswer.find(params[:poll_answer_id])
poll_participation = current_user.participate_in_poll!(target, answer) if target
respond_to do |format|
format.html { redirect_to :back }
format.mobile { redirect_to stream_path }
format.json { render json: poll_participation, :status => 201 }
end
rescue ActiveRecord::RecordInvalid
respond_to do |format|
format.html { redirect_to :back }
format.mobile { redirect_to stream_path }
format.json { render :nothing => true, :status => 403 }
format.json { head :forbidden }
end
end
@ -25,4 +22,4 @@ class PollParticipationsController < ApplicationController
current_user.find_visible_shareable_by_id(Post, params[:post_id]) || raise(ActiveRecord::RecordNotFound.new)
end
end
end
end

View file

@ -3,7 +3,7 @@
# the COPYRIGHT file.
class PostsController < ApplicationController
before_action :authenticate_user!, only: :destroy
before_action :authenticate_user!, only: %i(destroy mentionable)
before_action :set_format_if_malformed_from_status_net, only: :show
respond_to :html, :mobile, :json, :xml
@ -13,7 +13,7 @@ class PostsController < ApplicationController
end
rescue_from Diaspora::NotMine do
render text: I18n.t("posts.show.forbidden"), status: 403
render plain: I18n.t("posts.show.forbidden"), status: 403
end
def show
@ -22,12 +22,11 @@ class PostsController < ApplicationController
presenter = PostPresenter.new(post, current_user)
respond_to do |format|
format.html do
gon.post = presenter
gon.post = presenter.with_initial_interactions
render locals: {post: presenter}
end
format.mobile { render locals: {post: post} }
format.xml { render xml: DiasporaFederation::Salmon::XmlPayload.pack(Diaspora::Federation::Entities.post(post)) }
format.json { render json: presenter }
format.json { render json: presenter.with_interactions }
end
end
@ -37,23 +36,28 @@ class PostsController < ApplicationController
oembed = params.slice(:format, :maxheight, :minheight)
render json: OEmbedPresenter.new(post, oembed)
rescue
render nothing: true, status: 404
head :not_found
end
def interactions
def mentionable
respond_to do |format|
format.json {
post = post_service.find!(params[:id])
render json: PostInteractionPresenter.new(post, current_user)
if params[:id].present? && params[:q].present?
render json: post_service.mentionable_in_comment(params[:id], params[:q])
else
head :no_content
end
}
format.any { render nothing: true, status: 406 }
format.any { head :not_acceptable }
end
rescue ActiveRecord::RecordNotFound
head :not_found
end
def destroy
post_service.destroy(params[:id])
respond_to do |format|
format.json { render nothing: true, status: 204 }
format.json { head :no_content }
format.any { redirect_to stream_path }
end
end

View file

@ -48,7 +48,7 @@ class ProfilesController < ApplicationController
end
respond_to do |format|
format.js { render :nothing => true, :status => 200 }
format.js { head :ok }
format.any {
if current_user.getting_started?
redirect_to getting_started_path
@ -77,6 +77,6 @@ class ProfilesController < ApplicationController
def profile_params
params.require(:profile).permit(:first_name, :last_name, :gender, :bio,
:location, :searchable, :tag_string, :nsfw,
:public_details, date: %i(year month day)) || {}
:public_details, date: %i[year month day]).to_h || {}
end
end

View file

@ -31,7 +31,7 @@ class ReportController < ApplicationController
if report.save
render json: true, status: 200
else
render nothing: true, status: 409
head :conflict
end
end

View file

@ -5,9 +5,9 @@ class ResharesController < ApplicationController
def create
reshare = reshare_service.create(params[:root_guid])
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
render text: I18n.t("reshares.create.error"), status: 422
render plain: I18n.t("reshares.create.error"), status: 422
else
render json: ExtremePostPresenter.new(reshare, current_user), status: 201
render json: PostPresenter.new(reshare, current_user).with_interactions, status: 201
end
def index

View file

@ -1,24 +1,24 @@
class SearchController < ApplicationController
before_action :authenticate_user!
def search
if search_query.starts_with?('#')
if search_query.length > 1
respond_to do |format|
respond_to do |format|
format.json {redirect_to tags_path(:q => search_query.delete("#."))}
format.any {redirect_to tag_path(:name => search_query.delete("#."))}
end
else
flash[:error] = I18n.t('tags.show.none', :name => search_query)
redirect_to :back
redirect_back fallback_location: stream_path
end
else
redirect_to people_path(:q => search_query)
end
end
end
private
def search_query
@search_query ||= (params[:q] || params[:term] || '').strip
end

View file

@ -3,8 +3,8 @@
# the COPYRIGHT file.
class SessionsController < Devise::SessionsController
after_filter :reset_authentication_token, :only => [:create]
before_filter :reset_authentication_token, :only => [:destroy]
after_action :reset_authentication_token, only: [:create]
before_action :reset_authentication_token, only: [:destroy]
def reset_authentication_token
current_user.reset_authentication_token! unless current_user.nil?

View file

@ -47,14 +47,8 @@ class StatusMessagesController < ApplicationController
end
def create
normalized_params = params.merge(
services: normalize_services,
aspect_ids: normalize_aspect_ids,
public: normalize_public_flag
)
status_message = StatusMessageCreationService.new(current_user).create(normalized_params)
status_message = StatusMessageCreationService.new(current_user).create(normalize_params)
respond_to do |format|
format.html { redirect_to :back }
format.mobile { redirect_to stream_path }
format.json { render json: PostPresenter.new(status_message, current_user), status: 201 }
end
@ -71,9 +65,8 @@ class StatusMessagesController < ApplicationController
def handle_create_error(error)
logger.debug error
respond_to do |format|
format.html { redirect_to :back }
format.mobile { redirect_to stream_path }
format.json { render text: error.message, status: 403 }
format.json { render plain: error.message, status: 403 }
end
end
@ -89,8 +82,19 @@ class StatusMessagesController < ApplicationController
request.env["HTTP_REFERER"].include?("/people/" + current_user.guid)
end
def normalize_services
[*params[:services]].compact
def normalize_params
params.permit(
:location_address,
:location_coords,
:poll_question,
status_message: %i[text provider_display_name],
poll_answers: []
).to_h.merge(
services: [*params[:services]].compact,
aspect_ids: normalize_aspect_ids,
public: [*params[:aspect_ids]].first == "public",
photos: [*params[:photos]].compact
)
end
def normalize_aspect_ids
@ -102,10 +106,6 @@ class StatusMessagesController < ApplicationController
end
end
def normalize_public_flag
[*params[:aspect_ids]].first == "public"
end
def remove_getting_started
current_user.disable_getting_started
end

View file

@ -15,7 +15,7 @@ class TagFollowingsController < ApplicationController
name_normalized = ActsAsTaggableOn::Tag.normalize(params['name'])
if name_normalized.nil? || name_normalized.empty?
render :nothing => true, :status => 403
head :forbidden
else
@tag = ActsAsTaggableOn::Tag.find_or_create_by(name: name_normalized)
@tag_following = current_user.tag_followings.new(:tag_id => @tag.id)
@ -23,7 +23,7 @@ class TagFollowingsController < ApplicationController
if @tag_following.save
render :json => @tag.to_json, :status => 201
else
render :nothing => true, :status => 403
head :forbidden
end
end
end
@ -35,11 +35,11 @@ class TagFollowingsController < ApplicationController
if tag_following && tag_following.destroy
respond_to do |format|
format.any(:js, :json) { render :nothing => true, :status => 204 }
format.any(:js, :json) { head :no_content }
end
else
respond_to do |format|
format.any(:js, :json) {render :nothing => true, :status => 403}
format.any(:js, :json) { head :forbidden }
end
end
end

View file

@ -24,8 +24,8 @@ class TagsController < ApplicationController
end
else
respond_to do |format|
format.json{ render :nothing => true, :status => 422 }
format.html{ redirect_to tag_path('partytimeexcellent') }
format.json { head :unprocessable_entity }
format.html { redirect_to tag_path("partytimeexcellent") }
end
end
end

View file

@ -46,7 +46,7 @@ class UsersController < ApplicationController
flash[:error] = t("users.update.settings_not_updated")
end
redirect_to :back
redirect_back fallback_location: privacy_settings_path
end
def destroy
@ -60,7 +60,7 @@ class UsersController < ApplicationController
else
flash[:error] = t "users.destroy.no_password"
end
redirect_to :back
redirect_back fallback_location: edit_user_path
end
end
@ -150,16 +150,7 @@ class UsersController < ApplicationController
:auto_follow_back_aspect_id,
:getting_started,
:post_default_public,
email_preferences: %i(
someone_reported
also_commented
mentioned
comment_on_post
private_message
started_sharing
liked
reshared
)
email_preferences: UserPreference::VALID_EMAIL_TYPES.map(&:to_sym)
)
end
# rubocop:enable Metrics/MethodLength

View file

@ -1,69 +0,0 @@
# Copyright (c) 2010-2012, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
module AnalyticsHelper
def include_mixpanel
include_analytics "mixpanel" do
nonced_javascript_tag do
<<-JS.html_safe
(function(d,c){var a,b,g,e;a=d.createElement('script');a.type='text/javascript';a.async=!0;a.src=('https:'===d.location.protocol?'https:':'http:')+'//api.mixpanel.com/site_media/js/api/mixpanel.2.js';b=d.getElementsByTagName('script')[0];b.parentNode.insertBefore(a,b);c._i=[];c.init=function(a,d,f){var b=c;'undefined'!==typeof f?b=c[f]=[]:f='mixpanel';g='disable track track_pageview track_links track_forms register register_once unregister identify name_tag set_config'.split(' ');
for(e=0;e<g.length;e++)(function(a){b[a]=function(){b.push([a].concat(Array.prototype.slice.call(arguments,0)))}})(g[e]);c._i.push([a,d,f])};window.mixpanel=c})(document,[]);
mixpanel.init("#{AppConfig.privacy.mixpanel_uid}");
JS
end
end
end
def include_mixpanel_guid
return unless current_user
include_analytics "mixpanel" do
nonced_javascript_tag do
<<-JS.html_safe
mixpanel.name_tag("#{current_user.guid}");
JS
end
end
end
def chartbeat_head_block
return unless configured?("chartbeat")
nonced_javascript_tag("var _sf_startpt=(new Date()).getTime()")
end
def include_chartbeat
include_analytics "chartbeat" do
nonced_javascript_tag do
<<-JS.html_safe
var _sf_async_config = { uid: #{AppConfig.privacy.chartbeat_uid}, domain: "#{AppConfig.pod_uri.host}" };
(function() {
function loadChartbeat() {
window._sf_endpt = (new Date()).getTime();
var e = document.createElement('script');
e.setAttribute('language', 'javascript');
e.setAttribute('type', 'text/javascript');
e.setAttribute('src',
(('https:' == document.location.protocol) ? 'https://a248.e.akamai.net/chartbeat.download.akamai.com/102508/' : 'http://static.chartbeat.com/') +
'js/chartbeat.js');
document.body.appendChild(e);
};
var oldonload = window.onload;
window.onload = (typeof window.onload != 'function') ?
loadChartbeat : function() { oldonload(); loadChartbeat(); };
})();
JS
end
end
end
private
def include_analytics(service, &block)
return unless configured?(service)
yield block
end
def configured?(service)
AppConfig.privacy.send("#{service}_uid").present?
end
end

View file

@ -52,11 +52,13 @@ module ApplicationHelper
def jquery_include_tag
buf = []
if AppConfig.privacy.jquery_cdn?
version = Jquery::Rails::JQUERY_2_VERSION
version = Jquery::Rails::JQUERY_3_VERSION
buf << [javascript_include_tag("//code.jquery.com/jquery-#{version}.min.js")]
buf << [nonced_javascript_tag("!window.jQuery && document.write(unescape('#{j javascript_include_tag('jquery2')}'));")]
buf << [
nonced_javascript_tag("!window.jQuery && document.write(unescape('#{j javascript_include_tag('jquery3')}'));")
]
else
buf << [javascript_include_tag("jquery2")]
buf << [javascript_include_tag("jquery3")]
end
buf << [javascript_include_tag("jquery_ujs")]
buf << [nonced_javascript_tag("jQuery.ajaxSetup({'cache': false});")]

View file

@ -21,16 +21,6 @@ module InterimStreamHackinessHelper
end
end
def publisher_hidden_text
if params[:prefill].present?
params[:prefill]
elsif defined?(@stream)
@stream.publisher.prefill
else
nil
end
end
def from_group(post)
if defined?(@stream) && params[:controller] == 'multis'
@stream.post_from_group(post)

View file

@ -11,7 +11,7 @@ module MetaDataHelper
end
def default_image_url
asset_url "assets/branding/logos/asterisk.png"
asset_url("assets/branding/logos/asterisk.png", skip_pipeline: true)
end
def default_author_name

View file

@ -7,25 +7,30 @@ module MobileHelper
reshare = Reshare.where(author_id: current_user.person_id,
root_guid: absolute_root.guid).first
klass = reshare.present? ? "active" : "inactive"
link_to "", reshares_path(root_guid: absolute_root.guid),
link_to content_tag(:span, post.reshares.size, class: "count reshare-count"),
reshares_path(root_guid: absolute_root.guid),
title: t("reshares.reshare.reshare_confirmation", author: absolute_root.author_name),
class: "entypo-reshare reshare-action #{klass}"
else
content_tag :div, nil, class: "entypo-reshare reshare-action disabled"
content_tag :div,
content_tag(:span, post.reshares.size, class: "count reshare-count"),
class: "entypo-reshare reshare-action disabled"
end
else
content_tag :div, nil, class: "entypo-reshare reshare-action disabled"
content_tag :div,
content_tag(:span, post.reshares.size, class: "count reshare-count"),
class: "entypo-reshare reshare-action disabled"
end
end
def mobile_like_icon(post)
if current_user && current_user.liked?(post)
link_to "",
link_to content_tag(:span, post.likes.size, class: "count like-count"),
"#",
data: {url: post_like_path(post.id, current_user.like_for(post).id)},
class: "entypo-heart like-action active"
else
link_to "",
link_to content_tag(:span, post.likes.size, class: "count like-count"),
"#",
data: {url: post_likes_path(post.id)},
class: "entypo-heart like-action inactive"
@ -33,7 +38,9 @@ module MobileHelper
end
def mobile_comment_icon(post)
link_to "", new_post_comment_path(post), class: "entypo-comment comment-action inactive"
link_to content_tag(:span, post.comments.size, class: "count comment-count"),
new_post_comment_path(post),
class: "entypo-comment comment-action inactive"
end
def show_comments_link(post, klass="")
@ -43,15 +50,12 @@ module MobileHelper
entypo_class = "entypo-chevron-down"
end
if post.comments_count > 0
link_to "#{t('admins.stats.comments', count: post.comments_count)}<i class='#{entypo_class}'></i>".html_safe,
post_comments_path(post, format: "mobile"),
class: "show-comments #{klass}"
else
html = "<span class='show-comments'>"
html << t("admins.stats.comments", count: post.comments_count)
html << "</span>"
end
link_to safe_join([
t("admins.stats.comments", count: post.comments_count),
content_tag(:i, nil, class: entypo_class)
]),
post_comments_path(post, format: "mobile"),
class: "show-comments #{klass}"
end
def additional_photos

View file

@ -2,42 +2,46 @@ module NotificationsHelper
include PeopleHelper
include PostsHelper
def object_link(note, actors)
def object_link(note, actors_html)
target_type = note.popup_translation_key
actors_count = note.actors.size
opts = {actors: actors_html, count: note.actors.size}
if note.instance_of?(Notifications::Mentioned)
if post = note.linked_object
translation(target_type,
actors: actors,
count: actors_count,
post_link: link_to(post_page_title(post), post_path(post)).html_safe)
else
t(note.deleted_translation_key, :actors => actors, :count => actors_count).html_safe
if note.respond_to?(:linked_object)
if note.linked_object.nil? && note.respond_to?(:deleted_translation_key)
target_type = note.deleted_translation_key
elsif note.is_a?(Notifications::Mentioned)
opts.merge!(opts_for_mentioned(note.linked_object))
elsif %w(Notifications::CommentOnPost Notifications::AlsoCommented Notifications::Reshared Notifications::Liked)
.include?(note.type)
opts.merge!(opts_for_post(note.linked_object))
end
elsif note.instance_of?(Notifications::CommentOnPost) || note.instance_of?(Notifications::AlsoCommented) || note.instance_of?(Notifications::Reshared) || note.instance_of?(Notifications::Liked)
if post = note.linked_object
translation(target_type,
actors: actors,
count: actors_count,
post_author: h(post.author_name),
post_link: link_to(post_page_title(post),
post_path(post),
data: {ref: post.id},
class: "hard_object_link").html_safe)
else
t(note.deleted_translation_key, :actors => actors, :count => actors_count).html_safe
end
else #Notifications:StartedSharing, etc.
translation(target_type, :actors => actors, :count => actors_count)
end
translation(target_type, opts)
end
def translation(target_type, opts = {})
{:post_author => nil}.merge!(opts)
t("#{target_type}", opts).html_safe
end
def opts_for_post(post)
{
post_author: html_escape(post.author_name),
post_link: link_to(post_page_title(post),
post_path(post),
data: {ref: post.id},
class: "hard_object_link").html_safe
}
end
def opts_for_mentioned(mentioned)
post = mentioned.instance_of?(Comment) ? mentioned.parent : mentioned
{
post_link: link_to(post_page_title(post), post_path(post)).html_safe
}.tap {|opts|
opts[:comment_path] = post_path(post, anchor: mentioned.guid).html_safe if mentioned.instance_of?(Comment)
}
end
def notification_people_link(note, people=nil)
actors =people || note.actors
number_of_actors = actors.size

View file

@ -31,13 +31,13 @@ module UsersHelper
# specified from AVAILABLE_COLOR_THEMES in
# config/initializers/color_themes.rb.
#
# @example if AVAILABLE_COLOR_THEMES = {"original"=>"Original dark", "dark_green" => "Dark green"}
# @example if AVAILABLE_COLOR_THEMES = ["original", "dark_green"]
# available_color_themes
# #=> [["Original dark", "original"], ["Dark green", "dark_green"]]
# #=> [["Original gray", "original"], ["Dark green", "dark_green"]]
def available_color_themes
opts = []
AVAILABLE_COLOR_THEMES.map do |theme_code, theme_name|
opts << [theme_name, theme_code]
AVAILABLE_COLOR_THEMES.map do |theme_code|
opts << [I18n.t("color_themes.#{theme_code}"), theme_code]
end
opts
end

Some files were not shown because too many files have changed in this diff Show more