├── .tachikoma.yml ├── Procfile ├── .dockerignore ├── autotest └── discover.rb ├── public ├── img │ ├── next.png │ ├── prev.png │ ├── close.gif │ ├── close.png │ ├── loading.gif │ ├── masao.jpg │ ├── masao2.jpg │ ├── masao3.jpg │ ├── wakaruwa.png │ ├── masao_footer.jpg │ ├── wakaranaiwa.png │ ├── icons │ │ ├── 10 │ │ │ ├── alplha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 11 │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 12 │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 04 │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 05 │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 06 │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 07 │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 08 │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 09 │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 01 │ │ │ ├── alplha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 02 │ │ │ ├── alplha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 03 │ │ │ ├── alplha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── massr │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── mh4-2 │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── mh4-1 │ │ │ ├── favicon.ico │ │ │ ├── orginal.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── 03-yayoi │ │ │ ├── alplha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── default │ │ │ ├── favicon.ico │ │ │ ├── apple-touch-icon-57x57.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ └── massr-devel │ │ │ ├── alpha.png │ │ │ ├── favicon.ico │ │ │ ├── original.png │ │ │ ├── apple-touch-icon-114x114.png │ │ │ ├── apple-touch-icon-57x57.png │ │ │ └── apple-touch-icon-72x72.png │ ├── masao_iine_000.png │ ├── masao_iine_001.png │ ├── masao_loading.gif │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png ├── js │ └── watch_authorize.js └── default.json ├── views ├── notify.haml ├── responses.haml ├── footer.haml ├── not_found.haml ├── unauthorized.haml ├── login.haml ├── user_photos.haml ├── user_statements.haml ├── photos.haml ├── user_statement.haml ├── statements.haml ├── users.haml ├── photo.haml ├── sidebar.haml ├── admin.haml ├── head.haml ├── popup_photo.haml ├── index.haml ├── user.haml ├── statement.haml └── header.haml ├── assets ├── css │ ├── application.css │ ├── jquery.pnotify.default.icons.css │ ├── jquery.pnotify.default.css │ ├── magnific-popup.css │ └── default.css └── js │ ├── application.js │ ├── massr.plugin.js │ ├── plugins │ └── notify │ │ └── like.js │ ├── jquery.auto-link.js │ ├── jquery.masonry.min.js │ ├── jquery.purl.js │ └── massr.templates.js ├── config.ru ├── .travis.yml ├── .gitignore ├── config ├── mongoid.yml └── newrelic.yml ├── models ├── init.rb ├── like.rb ├── message.rb ├── search_pin.rb ├── plugin_setting.rb ├── stamp.rb ├── user.rb └── statement.rb ├── helpers ├── stats.rb ├── media.rb ├── resource.rb └── init.rb ├── app.json ├── routes ├── admin.rb ├── icon.rb ├── main.rb ├── stamp.rb ├── auth.rb ├── init.rb ├── search.rb ├── statement.rb └── user.rb ├── Rakefile ├── spec ├── models │ ├── like_spec.rb │ ├── plugin_setting_spec.rb │ ├── search_pin_spec.rb │ ├── statement_spec.rb │ └── user_spec.rb └── spec_helper.rb ├── Guardfile ├── plugins ├── icon │ └── rotate.rb ├── notify │ ├── information.rb │ └── like.rb ├── logging.rb ├── media │ ├── picasa.rb │ ├── gyazo.rb │ └── twitter.rb ├── cache │ └── memcached.rb └── async_request.rb ├── Dockerfile ├── Gemfile ├── massr.rb ├── bin └── mongo_tools.sh ├── README.md └── Gemfile.lock /.tachikoma.yml: -------------------------------------------------------------------------------- 1 | strategy: 'bundler' 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -p $PORT 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .env 3 | .ruby-version 4 | .git 5 | -------------------------------------------------------------------------------- /autotest/discover.rb: -------------------------------------------------------------------------------- 1 | Autotest.add_discovery { "rspec2" } 2 | -------------------------------------------------------------------------------- /public/img/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/next.png -------------------------------------------------------------------------------- /public/img/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/prev.png -------------------------------------------------------------------------------- /public/img/close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/close.gif -------------------------------------------------------------------------------- /public/img/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/close.png -------------------------------------------------------------------------------- /public/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/loading.gif -------------------------------------------------------------------------------- /public/img/masao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/masao.jpg -------------------------------------------------------------------------------- /public/img/masao2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/masao2.jpg -------------------------------------------------------------------------------- /public/img/masao3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/masao3.jpg -------------------------------------------------------------------------------- /public/img/wakaruwa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/wakaruwa.png -------------------------------------------------------------------------------- /public/img/masao_footer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/masao_footer.jpg -------------------------------------------------------------------------------- /public/img/wakaranaiwa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/wakaranaiwa.png -------------------------------------------------------------------------------- /views/notify.haml: -------------------------------------------------------------------------------- 1 | - notify_plugins.each do |notify| 2 | .alert.alert-info 3 | ! #{notify.render} 4 | -------------------------------------------------------------------------------- /public/img/icons/04/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/04/alpha.png -------------------------------------------------------------------------------- /public/img/icons/05/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/05/alpha.png -------------------------------------------------------------------------------- /public/img/icons/06/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/06/alpha.png -------------------------------------------------------------------------------- /public/img/icons/07/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/07/alpha.png -------------------------------------------------------------------------------- /public/img/icons/08/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/08/alpha.png -------------------------------------------------------------------------------- /public/img/icons/09/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/09/alpha.png -------------------------------------------------------------------------------- /public/img/icons/11/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/11/alpha.png -------------------------------------------------------------------------------- /public/img/icons/12/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/12/alpha.png -------------------------------------------------------------------------------- /public/img/masao_iine_000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/masao_iine_000.png -------------------------------------------------------------------------------- /public/img/masao_iine_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/masao_iine_001.png -------------------------------------------------------------------------------- /public/img/masao_loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/masao_loading.gif -------------------------------------------------------------------------------- /public/img/icons/01/alplha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/01/alplha.png -------------------------------------------------------------------------------- /public/img/icons/01/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/01/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/01/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/01/original.png -------------------------------------------------------------------------------- /public/img/icons/02/alplha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/02/alplha.png -------------------------------------------------------------------------------- /public/img/icons/02/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/02/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/02/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/02/original.png -------------------------------------------------------------------------------- /public/img/icons/03/alplha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03/alplha.png -------------------------------------------------------------------------------- /public/img/icons/03/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/03/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03/original.png -------------------------------------------------------------------------------- /public/img/icons/04/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/04/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/04/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/04/original.png -------------------------------------------------------------------------------- /public/img/icons/05/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/05/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/05/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/05/original.png -------------------------------------------------------------------------------- /public/img/icons/06/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/06/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/06/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/06/original.png -------------------------------------------------------------------------------- /public/img/icons/07/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/07/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/07/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/07/original.png -------------------------------------------------------------------------------- /public/img/icons/08/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/08/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/08/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/08/original.png -------------------------------------------------------------------------------- /public/img/icons/09/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/09/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/09/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/09/original.png -------------------------------------------------------------------------------- /public/img/icons/10/alplha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/10/alplha.png -------------------------------------------------------------------------------- /public/img/icons/10/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/10/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/10/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/10/original.png -------------------------------------------------------------------------------- /public/img/icons/11/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/11/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/11/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/11/original.png -------------------------------------------------------------------------------- /public/img/icons/12/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/12/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/12/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/12/original.png -------------------------------------------------------------------------------- /public/img/icons/massr/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr/alpha.png -------------------------------------------------------------------------------- /public/img/icons/mh4-2/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-2/alpha.png -------------------------------------------------------------------------------- /public/img/icons/massr/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/mh4-1/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-1/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/mh4-1/orginal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-1/orginal.png -------------------------------------------------------------------------------- /public/img/icons/mh4-2/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-2/favicon.ico -------------------------------------------------------------------------------- /public/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/img/icons/03-yayoi/alplha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03-yayoi/alplha.png -------------------------------------------------------------------------------- /public/img/icons/03-yayoi/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03-yayoi/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/default/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/default/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/massr/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr/original.png -------------------------------------------------------------------------------- /public/img/icons/mh4-2/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-2/original.png -------------------------------------------------------------------------------- /public/img/icons/03-yayoi/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03-yayoi/original.png -------------------------------------------------------------------------------- /public/img/icons/massr-devel/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr-devel/alpha.png -------------------------------------------------------------------------------- /public/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/img/icons/massr-devel/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr-devel/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/massr-devel/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr-devel/original.png -------------------------------------------------------------------------------- /public/img/icons/01/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/01/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/01/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/01/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/02/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/02/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/02/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/02/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/03/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/03/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/04/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/04/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/04/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/04/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/05/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/05/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/05/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/05/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/06/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/06/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/06/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/06/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/07/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/07/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/07/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/07/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/08/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/08/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/08/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/08/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/09/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/09/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/09/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/09/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/10/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/10/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/10/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/10/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/11/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/11/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/11/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/11/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/12/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/12/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/12/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/12/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/01/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/01/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/02/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/02/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/03/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/04/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/04/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/05/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/05/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/06/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/06/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/07/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/07/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/08/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/08/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/09/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/09/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/10/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/10/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/11/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/11/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/12/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/12/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/massr/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/massr/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/mh4-1/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-1/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/mh4-1/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-1/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/mh4-2/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-2/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/mh4-2/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-2/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/03-yayoi/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03-yayoi/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/03-yayoi/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03-yayoi/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/default/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/default/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/default/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/default/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /public/img/icons/massr/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/mh4-1/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-1/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/mh4-2/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/mh4-2/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/03-yayoi/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/03-yayoi/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/default/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/default/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/massr-devel/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr-devel/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /public/img/icons/massr-devel/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr-devel/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /public/img/icons/massr-devel/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasamas/massr/HEAD/public/img/icons/massr-devel/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /views/responses.haml: -------------------------------------------------------------------------------- 1 | %p 2 | - statement.refs.each do |res| 3 | - res.res = nil 4 | != haml :statement, :locals => {:statement => res, :is_new => false} 5 | -------------------------------------------------------------------------------- /assets/css/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | *= require bootstrap 3 | *= require bootstrap-responsive 4 | *= require jquery.pnotify.default 5 | *= require magnific-popup 6 | *= require default 7 | */ 8 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require './massr' 2 | 3 | if ENV['FORCE_HTTPS'] 4 | require 'rack/ssl' 5 | use Rack::SSL 6 | end 7 | 8 | $stdout.sync = true # for Heroku logging 9 | run Massr::App 10 | 11 | -------------------------------------------------------------------------------- /views/footer.haml: -------------------------------------------------------------------------------- 1 | .row-fluid 2 | %footer 3 | .span9 4 | %div< Massr (C) The wasam@s production 5 | != javascript_tag 'application' 6 | - if massr_settings 7 | %script 8 | Massr.setting_file = '#{massr_settings}'; 9 | -------------------------------------------------------------------------------- /views/not_found.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Masao Not Found - Massr 5 | != haml :head 6 | %body 7 | %h1<404 Masao Not Found 8 | .special-info 9 | %img#masao{:src => random_masao} 10 | .special-info 11 | %a{:href => '/'} random_masao} 10 | .special-info 11 | %a{:href => '/'} "/auth/twitter", :title => "Login"} {:statements => statements, :q => q, :pagenation => pagenation} 13 | .span3 14 | != haml :sidebar 15 | != haml :footer 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .ruby-version 6 | .env 7 | *.code-workspace 8 | coverage 9 | InstalledFiles 10 | lib/bundler/man 11 | pkg 12 | rdoc 13 | spec/reports 14 | test/tmp 15 | test/version_tmp 16 | tmp 17 | vendor 18 | public/custom.json 19 | public/custom_cache.json 20 | public/assets/ 21 | 22 | # YARD artifacts 23 | .yardoc 24 | _yardoc 25 | doc/ 26 | -------------------------------------------------------------------------------- /views/user_statements.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Massr - User Statements 5 | != haml :head 6 | %body 7 | != haml :header 8 | .container-fluid 9 | .row-fluid 10 | .span9 11 | .main 12 | != haml :statements ,:locals => {:res_ids => res_ids , :statements => statements, :q => q} 13 | .span3 14 | != haml :sidebar 15 | != haml :footer 16 | -------------------------------------------------------------------------------- /views/photos.haml: -------------------------------------------------------------------------------- 1 | #items 2 | - statements.each do |statement| 3 | != haml :photo , :locals => {:statement => statement} 4 | - if pagenation 5 | .pagenation 6 | %input.btn.btn-large{:id => 'subjoinpage', :type => "button" , :value => "next" , :path => "#{request.path_info}.json", :query => "#{q}"} 7 | %img{:id => 'subjoinpage-loading', :src => '/img/masao_loading.gif', :alt => 'Loading...'} 8 | 9 | -------------------------------------------------------------------------------- /views/user_statement.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Massr - Statement 5 | != haml :head 6 | %body 7 | != haml :header 8 | .container-fluid 9 | .row-fluid 10 | .span9 11 | .main 12 | %p 13 | != haml :statement, :locals => {:statement => statement, :is_new => false} 14 | != haml :responses, :locals => {:statement => statement} 15 | .span3 16 | != haml :sidebar 17 | != haml :footer 18 | -------------------------------------------------------------------------------- /config/mongoid.yml: -------------------------------------------------------------------------------- 1 | production: 2 | clients: 3 | default: 4 | uri: <%= ENV['MONGODB_URI'] || ENV['MONGOLAB_URI'] %> 5 | options: 6 | log_level: :fatal 7 | development: 8 | clients: 9 | default: 10 | uri: mongodb://localhost:27017/massr 11 | options: 12 | log_level: :info 13 | test: 14 | clients: 15 | default: 16 | uri: mongodb://localhost:27017/massr_test 17 | options: 18 | log_level: :info 19 | -------------------------------------------------------------------------------- /models/init.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # models/init.rb : initialize models 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | require_relative 'user' 12 | require_relative 'statement' 13 | require_relative 'stamp' 14 | require_relative 'like' 15 | #require_relative 'message' 16 | require_relative 'search_pin' 17 | require_relative 'plugin_setting' 18 | -------------------------------------------------------------------------------- /models/like.rb: -------------------------------------------------------------------------------- 1 | module Massr 2 | class Like 3 | include ::Mongoid::Document 4 | include ::Mongoid::Timestamps 5 | 6 | field :is_read, :type => Boolean 7 | validates_presence_of :is_read 8 | 9 | embedded_in :statement, class_name: 'Massr::Statement', inverse_of: :likes 10 | belongs_to :user, class_name: 'Massr::User' 11 | 12 | def to_hash 13 | { 14 | 'id' => id.to_s, 15 | 'user' => user.to_hash, 16 | } 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /helpers/stats.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # helpers/stats.rb : system statuses 4 | # 5 | # Copyright (C) 2014 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | module Massr 12 | class App < Sinatra::Base 13 | helpers do 14 | def stats(item) 15 | stats = Mongoid::Clients.default.database.command(dbStats: 1) 16 | return stats.documents[0][item.to_s] 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /views/statements.haml: -------------------------------------------------------------------------------- 1 | #statements 2 | - statements.each do |statement| 3 | != haml :statement , :locals => {:statement => statement, :is_new => (res_ids && res_ids.include?(statement._id))} 4 | .pagenation 5 | %input.btn.btn-large{:id => 'subjoinpage', :type => "button" , :value => "next" , :path => "#{request.path_info}#{request.path_info == '/' ? 'index' : ''}.json", :query => "#{q}"} 6 | %img{:id => 'subjoinpage-loading', :src => '/img/masao_loading.gif', :alt => 'Loading...'} 7 | 8 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Massr", 3 | "buildpacks": [ 4 | {"url": "heroku/jvm"}, 5 | {"url": "heroku/ruby"} 6 | ], 7 | "addons": [ 8 | "sendgrid", 9 | "memcachier", 10 | "mongolab" 11 | ], 12 | "env": { 13 | "TWITTER_CONSUMER_ID": { 14 | "required": true 15 | }, 16 | "TWITTER_CONSUMER_SECRET": { 17 | "required": true 18 | }, 19 | "TZ": "Asia/Tokyo", 20 | "LANG": "en_US.UTF-8", 21 | "RACK_ENV": "production" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /models/message.rb: -------------------------------------------------------------------------------- 1 | module Massr 2 | class Message 3 | include ::Mongoid::Document 4 | include ::Mongoid::Timestamps 5 | store_in collection: 'massr.messages' 6 | 7 | field :from_user_id, type: ObjectId 8 | field :to_user_id, type: ObjectId 9 | field :message, type: String 10 | validates_presence_of :from_user_id, :to_user_id, :message 11 | 12 | belongs_to :user, class_name: 'User', in: :from_user_id 13 | belongs_to :user, class_name: 'User', in: :to_user_id 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /views/users.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Massr - Mini Wassr - User List. 5 | != haml :head 6 | %body 7 | != haml :header 8 | .container 9 | .row-fluid 10 | .span8 11 | %p 12 | =session[:errors] 13 | - users.each do |user| 14 | %p 15 | %img{:SRC => "#{get_icon_url(user)}" ,:width => "45" ,:height => "45"} 16 | = user.name 17 | = user.massr_id 18 | = user.created_at.localtime 19 | .span4 20 | != haml :sidebar 21 | != haml :footer 22 | -------------------------------------------------------------------------------- /routes/admin.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # routes/admin.rb 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | module Massr 12 | class App < Sinatra::Base 13 | before '/admin*' do 14 | user = User.find_by(id: session[:user_id]) 15 | redirect to('/') unless user.admin? 16 | end 17 | 18 | get '/admin' do 19 | haml :admin, :locals => {users: User.where(:_id.ne => session[:user_id])} 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /assets/js/massr.plugin.js: -------------------------------------------------------------------------------- 1 | /* 2 | * massr.plugin.js : plugin setup 3 | * 4 | * Copyright (C) 2015 by The wasam@s production 5 | * 6 | * Distributed under GPL 7 | */ 8 | 9 | Massr.plugin_setup = function(name, opts){ 10 | var plugin = name.match(/^([^\/]+)\/([^ ]+) (.*)$/) || [name]; 11 | switch(plugin[1]){ 12 | case "notify": 13 | switch(plugin[2]){ 14 | case "information": 15 | break; 16 | case "like": 17 | Massr.plugin_notify_like(plugin[3], opts); 18 | break; 19 | } 20 | break; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /routes/icon.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # routes/icon.rb 4 | # 5 | # Copyright (C) 2014 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | module Massr 12 | class App < Sinatra::Base 13 | 14 | get '/favicon.ico' do 15 | send_file("public/img/icons/#{icon_dir}/favicon.ico") 16 | end 17 | 18 | get '/img/apple-touch-icon-:size.png' do 19 | send_file("public/img/icons/#{icon_dir}/apple-touch-icon-#{params[:size]}.png") 20 | end 21 | 22 | end 23 | end 24 | 25 | 26 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | 4 | begin 5 | require 'rspec/core/rake_task' 6 | desc 'Run the code in specs' 7 | RSpec::Core::RakeTask.new(:spec) do |t| 8 | t.pattern = "spec/**/*_spec.rb" 9 | end 10 | task :default => [:spec] 11 | rescue LoadError => e 12 | end 13 | 14 | require 'sinatra/asset_pipeline/task' 15 | require './massr' 16 | Sinatra::AssetPipeline::Task.define! Massr::App 17 | 18 | # Local Variables: 19 | # mode: ruby 20 | # indent-tabs-mode: t 21 | # tab-width: 3 22 | # ruby-indent-level: 3 23 | # End: 24 | # vim: ts=3 25 | -------------------------------------------------------------------------------- /assets/css/jquery.pnotify.default.icons.css: -------------------------------------------------------------------------------- 1 | /* 2 | Document : jquery.pnotify.default.icons.css 3 | Created on : Nov 24, 2009, 2:58:21 PM 4 | Author : Hunter Perrin 5 | Version : 1.2.0 6 | Link : http://pinesframework.org/pnotify/ 7 | Description: 8 | Pines Icon styling for Pines Notify. 9 | */ 10 | 11 | .ui-pnotify .picon { 12 | background-color: transparent; 13 | background-repeat: no-repeat; 14 | background-position: center center; 15 | width: 17px; 16 | height: 17px; 17 | } 18 | .ui-pnotify-title { 19 | line-height: 17px; 20 | min-height: 17px; 21 | } -------------------------------------------------------------------------------- /models/search_pin.rb: -------------------------------------------------------------------------------- 1 | module Massr 2 | class SearchPin 3 | include ::Mongoid::Document 4 | store_in collection: 'massr.search_pins' 5 | 6 | field :word, type: String 7 | field :label, type: String 8 | validates_presence_of :word, :label 9 | validates_uniqueness_of :word 10 | 11 | def self.create_by_word(word, label = nil) 12 | pin = SearchPin.create(word: word, label: label ? label : word) 13 | pin.save 14 | return pin 15 | end 16 | 17 | def label=(label) 18 | self[:label] = label 19 | save 20 | self 21 | end 22 | end 23 | end 24 | 25 | -------------------------------------------------------------------------------- /spec/models/like_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'models/like' 3 | require 'models/user' 4 | 5 | describe 'Massr::Like' do 6 | describe '#to_hash' do 7 | before :all do 8 | Massr::User.collection.drop 9 | @user = Massr::User.create_by_registration_form(prototype_user(0)) 10 | @like = Massr::Like.new(:user => @user) 11 | puts @like.to_json 12 | end 13 | subject{ @like.to_hash } 14 | 15 | it {is_expected.to be_a_kind_of(Hash)} 16 | it {expect(subject['id']).to be_a_kind_of(String)} 17 | it {expect(subject['user']).to be_a_kind_of(Hash)} 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /views/photo.haml: -------------------------------------------------------------------------------- 1 | .item{id: "st-#{statement.id}"} 2 | %div{:class => "#{session[:user_id] == statement.user.id ? "item-body-me item-body" : "item-body"}"} 3 | / 画像表示 4 | .item-photos 5 | - statement.photos.each do |photo| 6 | %a{:href => "##{statement.id}", :class => "popup-image"}< 7 | %img.item-photo{src: "#{photo}", alt: "#{statement.id}", title: "#{statement.id}"} 8 | != haml :popup_photo, locals: {statement: statement, photo: photo, detail: true} 9 | 10 | / 発言の付属情報 11 | .item-info 12 | at 13 | %a{href: "/statement/#{statement.id}"} 14 | = statement.created_at 15 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard :rspec, cmd: "bundle exec rspec" do 2 | require "guard/rspec/dsl" 3 | dsl = Guard::RSpec::Dsl.new(self) 4 | 5 | # Feel free to open issues for suggestions and improvements 6 | 7 | # RSpec files 8 | rspec = dsl.rspec 9 | watch(rspec.spec_helper) { rspec.spec_dir } 10 | watch(rspec.spec_support) { rspec.spec_dir } 11 | watch(rspec.spec_files) 12 | 13 | # Ruby files 14 | ruby = dsl.ruby 15 | watch('massr.rb') { rspec.spec_dir } 16 | watch(%r[^models/(?.+)\.rb$]){|m| "#{rspec.spec_dir}/models/#{m[:file]}_spec.rb"} 17 | dsl.watch_spec_files_for(ruby.lib_files) 18 | 19 | end 20 | -------------------------------------------------------------------------------- /views/sidebar.haml: -------------------------------------------------------------------------------- 1 | %aside 2 | .well.sidebar-nav 3 | -if current_user 4 | %h2 "/user/#{member.massr_id}"}< 14 | %img.massr-icon-mini{:src => "#{get_icon_url(member)}" ,:class => "massr_id", :alt=> member.name, :title => member.name} 15 | -------------------------------------------------------------------------------- /spec/models/plugin_setting_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'models/plugin_setting' 3 | 4 | describe 'Massr::PluginSetting' do 5 | describe '#get' do 6 | before :all do 7 | Massr::PluginSetting.collection.drop 8 | end 9 | 10 | it('exist key') do 11 | Massr::PluginSetting.set('test', 'aaa') 12 | expect(Massr::PluginSetting.get('test')).to eq('aaa') 13 | Massr::PluginSetting.set('test', 'bbb') 14 | expect(Massr::PluginSetting.get('test')).to eq('bbb') 15 | end 16 | 17 | it('no exist key') do 18 | Massr::PluginSetting.set('test', 'aaa') 19 | expect(Massr::PluginSetting.get('hoge')).to be nil 20 | end 21 | end 22 | end 23 | 24 | -------------------------------------------------------------------------------- /plugins/icon/rotate.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # plugins/icon/rotate.rb : massr icon plugin for monthly masao 4 | # 5 | # Copyright (C) 2014 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | # Usage: 11 | # "plugin": { 12 | # "icon/rotate" : {} 13 | # } 14 | # 15 | module Massr 16 | module Plugin::Icon 17 | class Rotate 18 | def initialize(plugin_id, opts) 19 | end 20 | end 21 | end 22 | 23 | class App < Sinatra::Base 24 | helpers do 25 | def icon_dir 26 | Time.now.strftime("%m") || SETTINGS['resource']['icon_dir'] || 'default' 27 | end 28 | end 29 | end 30 | end 31 | 32 | -------------------------------------------------------------------------------- /plugins/notify/information.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # plugins/notify/information.rb : massr notify plugin for information message on top of page 4 | # 5 | # Copyright (C) 2013 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | # Usage: 11 | # "plugin": { 12 | # "notify/information ID": { 13 | # "message": "message text here" 14 | # } 15 | # } 16 | # 17 | module Massr 18 | module Plugin::Notify 19 | class Information 20 | def initialize(plugin_id, opts) 21 | @message = opts['message'] || nil 22 | end 23 | 24 | def render 25 | @message 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /public/js/watch_authorize.js: -------------------------------------------------------------------------------- 1 | /* 2 | * watch_authorize.js : watching authorization status and redirect to top 3 | * 4 | * Copyright (C) 2012 by The wasam@s production 5 | * 6 | * Distributed under GPL 7 | */ 8 | 9 | $(function(){ 10 | setInterval(function(){ 11 | $.ajax({ 12 | url: '/', 13 | type: 'GET', 14 | dataType: 'HTML', 15 | cache: false, 16 | success: function(data) { 17 | var $masao = $('#masao', data); 18 | if($masao.length > 0) { // not authorized yet 19 | $('#masao').attr('src', $masao.attr('src')); 20 | } else { // already authorized 21 | location.href = '/'; 22 | } 23 | }, 24 | error: function(XMLHttpRequest, textStatus, errorThrown) { 25 | location.reload(); 26 | } 27 | }); 28 | }, 60000); 29 | }); 30 | -------------------------------------------------------------------------------- /models/plugin_setting.rb: -------------------------------------------------------------------------------- 1 | module Massr 2 | class PluginSetting 3 | include ::Mongoid::Document 4 | store_in collection: 'massr.plugin_settings' 5 | 6 | field :plugin, type: String 7 | field :key, type: String 8 | field :value, type: String 9 | validates_presence_of :plugin, :key, :value 10 | 11 | def self.set(key, value) 12 | set = where(plugin: plugin_name, key: key).first 13 | if set 14 | set.update_attribute(:value, value) 15 | else 16 | set = self.create(plugin: plugin_name, key: key, value: value) 17 | set.save 18 | end 19 | end 20 | 21 | def self.get(key) 22 | set = where(plugin: plugin_name, key: key).first 23 | set ? set[:value] : nil 24 | end 25 | 26 | private 27 | def self.plugin_name 28 | path = caller[1].split(/:/).first 29 | return path.scan(%r|plugins/(.*)\.rb|).flatten.first || path 30 | end 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /plugins/logging.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | require 'logger' 3 | 4 | # 5 | # Massr Logging plugin 6 | # 7 | 8 | module Massr 9 | module Plugin 10 | class Logging 11 | include Singleton 12 | 13 | FATAL = Logger::FATAL 14 | ERROR = Logger::ERROR 15 | WARN = Logger::WARN 16 | INFO = Logger::INFO 17 | DEBUG = Logger::DEBUG 18 | 19 | def info(msg) 20 | @logger.info(msg) 21 | end 22 | 23 | def fatal(msg) 24 | @logger.fatal(msg) 25 | end 26 | 27 | def error(msg) 28 | @logger.error(msg) 29 | end 30 | 31 | def warn(msg) 32 | @logger.warn(msg) 33 | end 34 | 35 | def debug(msg) 36 | @logger.debug(msg) 37 | end 38 | 39 | def level(lv) 40 | @logger.level = lv 41 | end 42 | 43 | def initialize 44 | @logger ||= Logger.new(STDOUT) 45 | @logger.formatter = proc { |severity, datetime, progname, msg| 46 | "#{datetime}: #{severity} #{msg}\n" 47 | } 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Dockerfile for massr 3 | # 4 | # need some ENVs: 5 | # MONGODB_URI 6 | # MEMCACHE_SERVERS 7 | # TWITTER_CONSUMER_ID 8 | # TWITTER_CONSUMER_SECRET 9 | # 10 | # and some optional ENVs: 11 | # MASSR_SETTINGS 12 | # FULL_HOST (for internal of reverse proxy) 13 | # MEDIA_* (for media plugins) 14 | # 15 | FROM ruby:2.5 16 | MAINTAINER tdtds 17 | 18 | RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ 19 | && apt install -y nodejs openjdk-8-jre \ 20 | && apt -y clean \ 21 | && echo -e "install: --no-document\nupdate: --no-document" >/etc/gemrc \ 22 | && mkdir -p /opt/massr 23 | 24 | ENV LANG=ja_JP.utf8 25 | ENV RACK_ENV=production 26 | WORKDIR /opt/massr 27 | COPY ["Gemfile", "Gemfile.lock", "/opt/massr/"] 28 | RUN bundle --path=vendor/bundle --without=development:test --jobs=4 --retry=3 29 | 30 | COPY [".", "/opt/massr/"] 31 | RUN bundle exec rake assets:precompile 32 | 33 | EXPOSE 9393 34 | ENTRYPOINT ["bundle", "exec", "puma", "--port", "9393"] 35 | -------------------------------------------------------------------------------- /spec/models/search_pin_spec.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | require 'spec_helper' 3 | require 'models/search_pin' 4 | 5 | describe 'Massr::SearchPin', :type => :model do 6 | describe '.create_by_word' do 7 | before :all do 8 | Massr::SearchPin.collection.drop 9 | @pin = Massr::SearchPin.create_by_word('aaa') 10 | end 11 | subject{ @pin } 12 | 13 | describe '#word' do 14 | subject { super().word } 15 | it {is_expected.to eq('aaa')} 16 | end 17 | 18 | describe '#label' do 19 | subject { super().label } 20 | it {is_expected.to eq('aaa')} 21 | end 22 | end 23 | 24 | describe '#label=' do 25 | before :all do 26 | Massr::SearchPin.collection.drop 27 | @pin = Massr::SearchPin.create_by_word('aaa') 28 | @pin.label = 'bbb' 29 | end 30 | subject{ @pin } 31 | 32 | describe '#word' do 33 | subject { super().word } 34 | it {is_expected.to eq('aaa')} 35 | end 36 | 37 | describe '#label' do 38 | subject { super().label } 39 | it {is_expected.to eq('bbb')} 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /routes/main.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # routes/main.rb 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | module Massr 12 | class App < Sinatra::Base 13 | get '/' do 14 | haml :index , :locals => { 15 | statements: Statement.get_statements(param_date), 16 | q: nil} 17 | end 18 | 19 | get '/index.json' do 20 | main = cache.get('main') 21 | if(main && !params[:date]) 22 | main 23 | else 24 | json = [].tap {|a| 25 | Statement.get_statements(param_date).each do |statement| 26 | a << statement.to_hash 27 | end 28 | }.to_json 29 | cache.set('main', json) 30 | json 31 | end 32 | end 33 | 34 | delete '/newres' do 35 | access_user = User.find_by(id: session[:user_id]) 36 | res_ids = access_user.res_ids 37 | access_user.clear_res_ids 38 | end 39 | 40 | get '/ressize.json' do 41 | access_user = User.find_by(id: session[:user_id]) 42 | ressize = { 43 | :user => access_user.massr_id, 44 | :size => access_user.res_ids.size 45 | }.to_json 46 | end 47 | 48 | end 49 | end 50 | 51 | 52 | -------------------------------------------------------------------------------- /views/admin.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Massr - Mini Wassr 5 | != haml :head 6 | %body 7 | != haml :header 8 | .container-fluid 9 | .row-fluid 10 | .span9 11 | .main 12 | %h2<#{_member} 13 | %ul.user-list.admin 14 | - users.each do |member| 15 | %li{:class => "#{member.admin? ? 'admin' : 'normal'} #{member.authorized? ? 'authorized' : 'unauthorized'}", :id => member.massr_id} 16 | %a.admin 17 | %i.icon-star{:title => _unprivilege_user} 18 | %a.normal 19 | %i.icon-star.icon-white{:title => _privilege_user} 20 | %a.authorized 21 | %i.icon-remove{:title => _unauthorize_user} 22 | %a.unauthorized 23 | %i.icon-ok{:title => _authorize_user} 24 | %img{:src => get_icon_url(member) ,:class => "massr-icon", :alt => member.massr_id} 25 | = member.name 26 | %a.delete 27 | %i.icon-trash{:title => _delete_user} 28 | %h2 size || photo.rows > size 24 | photo.resize_to_fit!(size, size) 25 | photo.write(path) 26 | end 27 | if square 28 | img = Magick::Image.new(size, size) 29 | img.background_color = '#ffffff' 30 | img.composite!(photo, Magick::CenterGravity, Magick::OverCompositeOp) 31 | img.format = photo.format 32 | img.write(path) 33 | img.destroy! 34 | end 35 | photo.destroy! 36 | end 37 | 38 | def upload_file(path, content_type, display_size = nil); end 39 | end 40 | end 41 | end 42 | 43 | -------------------------------------------------------------------------------- /plugins/cache/memcached.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # plugins/cache/memcached.rb : massr cache plugin of memcached 4 | # 5 | # Copyright (C) 2016 by The WASAM@S Production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | require 'celluloid' 11 | 12 | module Massr 13 | module Plugin::Cache 14 | class Memcached 15 | include Celluloid 16 | 17 | def initialize(label, opts = {}) 18 | if ENV['MEMCACHE_SERVERS'] || ENV["MEMCACHIER_SERVERS"] 19 | @cache = Dalli::Client.new( 20 | ENV['MEMCACHE_SERVERS'] || ENV["MEMCACHIER_SERVERS"], 21 | :username => ENV['MEMCACHE_USERNAME'] || ENV["MEMCACHIER_USERNAME"], 22 | :password => ENV['MEMCACHE_PASSWORD'] || ENV["MEMCACHIER_PASSWORD"], 23 | :expires_in => 24 * 60 * 60, 24 | :compress => true) 25 | else 26 | @cache = Dalli::Client.new( 27 | nil, 28 | :expires_in => 24 * 60 * 60, 29 | :compress => true) 30 | end 31 | end 32 | 33 | def get(key) 34 | @cache.get(key) 35 | end 36 | 37 | def set(key, value) 38 | @cache.set(key, value) 39 | end 40 | 41 | def delete(key) 42 | @cache.delete(key) 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /views/head.haml: -------------------------------------------------------------------------------- 1 | %meta{:name => "viewport", :content => "width=device-width, initial-scale=1.0, user-scalable=no"} 2 | %meta{csrf_meta} 3 | %meta{:name => "format-detection", :content => "telephone=no"} 4 | != stylesheet_tag 'application' 5 | %link{:rel => "apple-touch-icon", :href => "/img/apple-touch-icon-114x114.png"} 6 | %link{:rel => "apple-touch-icon", :href => "/img/apple-touch-icon-57x57.png", :sizes => "57x57"} 7 | %link{:rel => "apple-touch-icon", :href => "/img/apple-touch-icon-72x72.png", :sizes => "72x72"} 8 | %link{:rel => "apple-touch-icon", :href => "/img/apple-touch-icon-114x114.png", :sizes => "114x114"} 9 | :css 10 | .statement-stamp .stamp-style{ 11 | height:#{SETTINGS['setting']['stamp_size']}px !important; 12 | width:#{SETTINGS['setting']['stamp_size']}px !important; 13 | } 14 | .statement-stamp .stamp-style .stamp img{ 15 | min-height:#{SETTINGS['setting']['stamp_size']}px !important; 16 | } 17 | .submit-stamp .stamp-style{ 18 | height:#{SETTINGS['setting']['stamp_thumbnail_size']}px !important; 19 | width:#{SETTINGS['setting']['stamp_thumbnail_size']}px !important; 20 | } 21 | .submit-stamp .stamp-style .stamp img{ 22 | min-height:#{SETTINGS['setting']['stamp_thumbnail_size']}px !important; 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /routes/stamp.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # routes/stamp.rb 4 | # 5 | # Copyright (C) 2015 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | module Massr 12 | class App < Sinatra::Base 13 | before '/stamp' do 14 | cache.delete('stamp') unless request.get? 15 | end 16 | 17 | post '/stamp' do 18 | @stamp = Stamp.new 19 | @stamp.update_stamp( request ) unless (request[:image_url].size == 0 || request[:statement_id].size == 0) 20 | end 21 | 22 | delete '/stamp' do 23 | Stamp.delete_stamp(params[:image_url]) 24 | redirect to('/') 25 | end 26 | 27 | after '/stamp' do 28 | cache.set('stamp', Stamp.get_stamps.map{|i| i.to_hash}) unless request.get? 29 | end 30 | 31 | get '/stamps' do 32 | haml :user_photos, locals: { 33 | statements: cache.get('stamp').map {|s| Statement.new(s['original'])}, 34 | q: nil, 35 | pagenation: false 36 | } 37 | end 38 | 39 | post '/stamp/tag' do 40 | @stamp = Stamp.find_by(id: request[:stamp_id]) 41 | @stamp.update_tag( request ) unless @stamp.nil? 42 | cache.delete('stamp') 43 | cache.set('stamp', Stamp.get_stamps.map{|i| i.to_hash}) 44 | @stamp.to_hash.to_json 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /plugins/async_request.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # plugins/async_request.rb : massr plugin of async request 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | require 'uri' 11 | require 'net/http' 12 | require 'net/https' 13 | require 'thread' 14 | require 'celluloid' 15 | 16 | 17 | module Massr 18 | module Plugin 19 | class AsyncRequest 20 | include Celluloid 21 | 22 | def initialize(uri) 23 | @uri = uri 24 | 25 | proxy = if @uri.scheme == 'https' 26 | URI(ENV['https_proxy'] || '') 27 | else 28 | URI(ENV['http_proxy'] || '') 29 | end 30 | @nethttp = Net::HTTP::Proxy(proxy.host, proxy.port).new( @uri.host, @uri.port ) 31 | @nethttp.use_ssl = true if @uri.scheme == 'https' 32 | end 33 | 34 | def add_photo(statement_id) 35 | @statement_id = statement_id 36 | begin 37 | @nethttp.start do |http| 38 | response = http.head( @uri.request_uri ) 39 | Statement.add_photo(@statement_id, @uri) if response["content-type"].to_s.include?('image') 40 | end 41 | rescue SocketError => e 42 | #URLの先が存在しないなど。 43 | rescue Timeout::Error => e 44 | #タイムアウト 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby '~> 2.6.0' 4 | 5 | gem 'sinatra', '>= 1.4.8', require: 'sinatra/base' 6 | gem 'sinatra-asset-pipeline', require: 'sinatra/asset_pipeline' 7 | gem 'sprockets-helpers' 8 | gem 'uglifier' 9 | gem 'yui-compressor' 10 | 11 | gem 'puma' 12 | gem 'hamlit', require: 'hamlit' 13 | gem 'omniauth', require: 'omniauth' 14 | gem 'omniauth-twitter', require: 'omniauth-twitter' 15 | gem 'mongoid', '~> 6.0', require: 'mongoid' 16 | gem 'bson_ext' 17 | gem 'rack_csrf', require: 'rack/csrf' 18 | gem 'rack-ssl', require: 'rack/ssl' 19 | gem 'mail', require: 'mail' 20 | gem 'twitter' 21 | gem 'gyazo' 22 | gem 'httparty', '~> 0.15.0' # for error in gyazo 23 | gem 'rmagick' 24 | gem 'dalli', require: ['dalli', 'rack/session/dalli'] 25 | gem 'celluloid' 26 | gem 'memcachier', require: 'memcachier' 27 | 28 | source 'https://rails-assets.org' do 29 | gem 'rails-assets-jquery' 30 | gem 'rails-assets-bootstrap', '~> 2.3.0' 31 | gem 'rails-assets-magnific-popup' 32 | end 33 | 34 | group :development, :test do 35 | gem 'rake' 36 | gem 'guard-rspec' 37 | gem 'fuubar' 38 | gem 'sinatra-contrib', require: 'sinatra/reloader' 39 | gem 'dotenv', require: 'dotenv' 40 | gem 'pry' 41 | gem 'therubyracer' 42 | end 43 | 44 | group :production do 45 | gem 'newrelic_rpm' 46 | end 47 | -------------------------------------------------------------------------------- /helpers/media.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # helpers/picasa.rb : helper methods of upload to picasa album 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | module Massr 11 | class NoPhotoError < StandardError; end 12 | 13 | class App < Sinatra::Base 14 | helpers do 15 | def specify_content_type(head) 16 | image_type = head.scan(/(image\/\w+)/) 17 | if image_type.length > 0 18 | return image_type[0][0] 19 | end 20 | # octet-stream 21 | ext = head.scan(/filename=\"[^\"]*\.([^\"\.]+)\"/) 22 | if ext.length > 0 23 | return 'image/' + ext[0][0].downcase 24 | end 25 | # not found 26 | return '' 27 | end 28 | 29 | def media_upload(photo_info, size=0, square=false) 30 | begin 31 | media_client = media_plugins.first 32 | raise Massr::NoPhotoError.new unless media_client 33 | 34 | path = photo_info[:tempfile].to_path || '' 35 | content_type = specify_content_type(photo_info[:head]) 36 | media_client.resize_file(path, size, square) 37 | return media_client.upload_file(path, content_type, SETTINGS['setting']['display_photo_size']) 38 | rescue StandardError 39 | raise Massr::NoPhotoError 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /plugins/media/gyazo.rb: -------------------------------------------------------------------------------- 1 | # plugins/media/gyazo.rb : massr plugin of upload image to gyazo 2 | # 3 | # usage: set Gyazo API's access token to ENV['MEDIA_GYAZO_SECRET'] 4 | # 5 | # Copyright (C) 2017 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | require 'gyazo' 11 | 12 | # 13 | # Massr Gyazo media plugin 14 | # 15 | module Massr 16 | module Plugin::Media 17 | class Gyazo 18 | DEFAULT_DISPLAY_PHOTO_SIZE = 800 19 | 20 | def initialize(label, opts) 21 | unless ENV['MEDIA_GYAZO_SECRET'] 22 | raise StandardError::new('MEDIA_GYAZO_SECRET not found') 23 | end 24 | init_client 25 | end 26 | 27 | def resize_file(path, size=0, square=false) 28 | # NOP because no limit size in Gyazo 29 | end 30 | 31 | def upload_file(path, content_type, display_size = nil) 32 | display_size ||= DEFAULT_DISPLAY_PHOTO_SIZE 33 | retry_count = 0 34 | begin 35 | res = @client.upload(path.to_s) 36 | return res['url'].sub(%r|^https://i\.gyazo\.com|, "https://i.gyazo.com/thumb/#{display_size}") 37 | rescue 38 | init_client 39 | retry if (retry_count += 1) < 10 40 | raise 41 | end 42 | end 43 | 44 | private 45 | def init_client 46 | @client = ::Gyazo::Client.new ENV['MEDIA_GYAZO_SECRET'] 47 | end 48 | end 49 | end 50 | end 51 | 52 | -------------------------------------------------------------------------------- /routes/auth.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # routes/auth.rb : login, logout and auth 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | module Massr 12 | class App < Sinatra::Base 13 | get '/login' do 14 | haml :login 15 | end 16 | 17 | get '/logout' do 18 | session.clear 19 | redirect to('/') 20 | end 21 | 22 | get '/auth/twitter/callback' do 23 | session.clear 24 | info = request.env['omniauth.auth'] 25 | session[:twitter_name] = info['extra']['raw_info']['name'] 26 | session[:twitter_id] = info['extra']['raw_info']['screen_name'] 27 | session[:twitter_user_id] = info['extra']['raw_info']['id'] 28 | session[:twitter_icon_url] = info['extra']['raw_info']['profile_image_url'] 29 | session[:twitter_icon_url_https] = info['extra']['raw_info']['profile_image_url_https'] 30 | end 31 | 32 | after '/auth/twitter/callback' do 33 | ##登録済みチェック 34 | user = ( 35 | User.find_by(twitter_user_id: session[:twitter_user_id]) or 36 | User.find_by(twitter_id: session[:twitter_id])) 37 | if user 38 | session[:user_id] = user._id 39 | redirect to('/') 40 | else 41 | redirect to('/user') 42 | end 43 | end 44 | 45 | get '/unauthorized' do 46 | haml :unauthorized 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /views/popup_photo.haml: -------------------------------------------------------------------------------- 1 | .mfp-hide.popup-photo{id: "#{statement.id}"} 2 | .stamp 3 | - stamp = get_stamp(photo) 4 | - if stamp 5 | %a.unusestamp 6 | %i.icon-remove-circle{title: _unuse_stamp} 7 | - else 8 | %a.usestamp 9 | %i.icon-ok-circle{title: _use_stamp } 10 | .image 11 | %a{href: "#{photo}", target: "_blank"} 12 | %img{src: "#{photo}" , alt: "#{statement.id}" , title: "#{statement.id}"} 13 | - if stamp 14 | .tag{style: "width: fit-content"} 15 | %span Tag: 16 | %input#stamp-tag{type: "text", value: "#{stamp.tag}", :"data-stamp-id" => stamp.id} 17 | %button#update-stamp-tag{class: "btn btn-small submit"} 更新 18 | - if detail 19 | .statement{id: "st-#{statement.id}"} 20 | / アイコン 21 | .statement-icon 22 | %a{href: "/user/#{statement.user.massr_id}"} 23 | %img.massr-icon{src: get_icon_url(statement.user)} 24 | 25 | %div{class: "#{session[:user_id] == statement.user.id ? "statement-body-me statement-body" : "statement-body"}"} 26 | / 発言本文 27 | .statement-message< 28 | = statement['body'].gsub(/[\r\n]+/, "\r") 29 | / 発言の付属情報 30 | .statement-info 31 | by 32 | %a{href: "/user/#{statement.user.massr_id}"}<#{statement.user.name} 33 | at 34 | %a{href: "/statement/#{statement.id}"} 35 | = statement['created_at'] 36 | - statement.likes.delete_if{|like| !like.user} 37 | -------------------------------------------------------------------------------- /routes/init.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # routes/init.rb : initialize routes 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | module Massr 12 | class App < Sinatra::Base 13 | before do 14 | cache.delete('main') unless request.get? 15 | cache.set('stamp', Stamp.get_stamps.map{|i| i.to_hash}) unless cache.get('stamp') 16 | 17 | case request.path 18 | when '/unauthorized' 19 | when '/login' 20 | when '/logout' 21 | when %r|^/auth/| 22 | when '/user' 23 | redirect to('/login') unless session[:twitter_user_id] 24 | else 25 | unless session[:user_id] 26 | redirect to('/login') 27 | else 28 | user = User.find_by(id: session[:user_id]) 29 | redirect to('/logout') unless user 30 | redirect to('/logout') if user.twitter_user_id == nil && user.twitter_id != session[:twitter_id] 31 | redirect to('/logout') unless session[:twitter_icon_url_https] 32 | redirect to('/user?update=true') unless user.twitter_user_id 33 | redirect to('/user?update=true') unless user.twitter_icon_url_https 34 | redirect to('/unauthorized') unless user.authorized? 35 | end 36 | end 37 | end 38 | 39 | not_found do 40 | haml :not_found 41 | end 42 | end 43 | end 44 | 45 | require_relative 'main' 46 | require_relative 'auth' 47 | require_relative 'statement' 48 | require_relative 'stamp' 49 | require_relative 'user' 50 | require_relative 'admin' 51 | require_relative 'search' 52 | require_relative 'icon' 53 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # spec_helper.rb 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | $:.unshift File.expand_path(File.join(File.dirname(__FILE__), '..')).untaint 12 | Bundler.require(:default, :test) if defined?(Bundler) 13 | 14 | RSpec.configure do |config| 15 | Mongoid::load!('config/mongoid.yml', :test) 16 | config.before(:all) do 17 | Mongoid::Clients.default.database.drop 18 | end 19 | end 20 | 21 | def prototype_user(no) 22 | [ 23 | { 24 | :massr_id => 'wasamas', 25 | :twitter_user_id => '00000000', 26 | :twitter_id => '1234567', 27 | :twitter_icon_url => 'http://example.com/foo1.png', 28 | :twitter_icon_url_https => 'https://example.com/foo1.png', 29 | :name => 'わさます', 30 | :email => 'wasamas@example.com', 31 | }, 32 | { 33 | :massr_id => 'wasamas2', 34 | :twitter_user_id => '11111111', 35 | :twitter_id => '7654321', 36 | :twitter_icon_url => 'http://example.com/foo2.png', 37 | :twitter_icon_url_https => 'https://example.com/foo2.png', 38 | :name => 'わさます2', 39 | :email => 'wasamas2@example.com', 40 | } 41 | ][no] 42 | end 43 | 44 | def prototype_statement(no, user) 45 | [ 46 | { 47 | # 元エントリ用 48 | :body => 'ほんぶんだよ!', 49 | :photo => 'http://example.com/foo.jpg', 50 | :photos => ['http://example.com/foo.jpg'], 51 | :user => user 52 | }, 53 | { 54 | # レスエントリ用 55 | :body => 'ほんぶんだよ2!', 56 | :photo => 'http://example.com/baa.jpg', 57 | :photos => ['http://example.com/baa.jpg'], 58 | :user => user 59 | }, 60 | def url 61 | "http://localhost/statement" 62 | end 63 | ][no] 64 | end 65 | 66 | -------------------------------------------------------------------------------- /models/stamp.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'uri' 3 | 4 | module Massr 5 | class Stamp 6 | include ::Mongoid::Document 7 | include ::Mongoid::Timestamps 8 | store_in collection: "massr.stamps" 9 | 10 | field :image_url, type: String 11 | field :popular, type: Integer, :default => 0 12 | field :tag, type: String 13 | validates_presence_of :image_url 14 | validates_uniqueness_of :image_url 15 | 16 | belongs_to :original, class_name: 'Massr::Statement', inverse_of: :stamp_source 17 | 18 | def self.get_stamps 19 | all = self.all 20 | if block_given? 21 | return yield(all) 22 | else 23 | return all.order_by(popular: 'desc') 24 | end 25 | end 26 | 27 | def self.get_statements(&block) 28 | stamps = get_stamps(&block) 29 | statements = [] 30 | 31 | stamps.each do |stamp| 32 | original = stamp.original 33 | if original.is_a? Massr::Statement 34 | statements << original 35 | else 36 | stamp.delete 37 | end 38 | end 39 | return statements 40 | end 41 | 42 | def self.delete_stamp(url,options={}) 43 | stamp = self.find_by(image_url: url) 44 | stamp.delete 45 | end 46 | 47 | def update_stamp(request) 48 | self.image_url = request[:image_url] 49 | statement = Statement.find_by(id: request[:statement_id]) 50 | self.original = statement 51 | save! 52 | return self 53 | end 54 | 55 | def post_stamp() 56 | self.popular += 1 57 | save! 58 | return self 59 | end 60 | 61 | def update_tag(request) 62 | self.tag = request[:tag] 63 | save! 64 | return self 65 | end 66 | 67 | def to_hash 68 | original = Statement.find_by(id: original_id) 69 | { 70 | 'id' => id.to_s, 71 | 'created_at' => created_at.localtime.strftime('%Y-%m-%d %H:%M:%S'), 72 | 'image_url' => image_url, 73 | 'tag' => tag, 74 | 'popular' => popular, 75 | 'original' => original ? original.to_hash : nil 76 | } 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /assets/css/jquery.pnotify.default.css: -------------------------------------------------------------------------------- 1 | /* 2 | Document : jquery.pnotify.default.css 3 | Created on : Nov 23, 2009, 3:14:10 PM 4 | Author : Hunter Perrin 5 | Version : 1.2.0 6 | Link : http://pinesframework.org/pnotify/ 7 | Description: 8 | Default styling for Pines Notify jQuery plugin. 9 | */ 10 | /* -- Notice */ 11 | .ui-pnotify { 12 | top: 25px; 13 | right: 25px; 14 | position: absolute; 15 | height: auto; 16 | /* Ensures notices are above everything */ 17 | z-index: 9999; 18 | } 19 | /* Hides position: fixed from IE6 */ 20 | html > body .ui-pnotify { 21 | position: fixed; 22 | } 23 | .ui-pnotify .ui-pnotify-shadow { 24 | -webkit-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5); 25 | -moz-box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5); 26 | box-shadow: 0px 2px 10px rgba(50, 50, 50, 0.5); 27 | } 28 | .ui-pnotify-container { 29 | background-position: 0 0; 30 | padding: .8em; 31 | height: 100%; 32 | margin: 0; 33 | } 34 | .ui-pnotify-sharp { 35 | -webkit-border-radius: 0; 36 | -moz-border-radius: 0; 37 | border-radius: 0; 38 | } 39 | .ui-pnotify-closer, .ui-pnotify-sticker { 40 | float: right; 41 | margin-left: .2em; 42 | } 43 | .ui-pnotify-title { 44 | display: block; 45 | margin-bottom: .4em; 46 | } 47 | .ui-pnotify-text { 48 | display: block; 49 | } 50 | .ui-pnotify-icon, .ui-pnotify-icon span { 51 | display: block; 52 | float: left; 53 | margin-right: .2em; 54 | } 55 | /* -- History Pulldown */ 56 | .ui-pnotify-history-container { 57 | position: absolute; 58 | top: 0; 59 | right: 18px; 60 | width: 70px; 61 | border-top: none; 62 | padding: 0; 63 | -webkit-border-top-left-radius: 0; 64 | -moz-border-top-left-radius: 0; 65 | border-top-left-radius: 0; 66 | -webkit-border-top-right-radius: 0; 67 | -moz-border-top-right-radius: 0; 68 | border-top-right-radius: 0; 69 | /* Ensures history container is above notices. */ 70 | z-index: 10000; 71 | } 72 | .ui-pnotify-history-container .ui-pnotify-history-header { 73 | padding: 2px; 74 | } 75 | .ui-pnotify-history-container button { 76 | cursor: pointer; 77 | display: block; 78 | width: 100%; 79 | } 80 | .ui-pnotify-history-container .ui-pnotify-history-pulldown { 81 | display: block; 82 | margin: 0 auto; 83 | } -------------------------------------------------------------------------------- /public/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugin": { 3 | }, 4 | "resource": { 5 | "icon_dir": "default" 6 | }, 7 | "setting": { 8 | "upload_photo_size": 2048, 9 | "display_photo_size": 800, 10 | "stamp_size": 200, 11 | "stamp_thumbnail_size": 100 12 | }, 13 | "local": { 14 | "site_name": "Massr", 15 | "like": "わかるわ", 16 | "unlike": "わからないわ", 17 | "delete": "削除", 18 | "res": "レス", 19 | "post": "投稿にょわー", 20 | "post_res": "レスるわ", 21 | "search": "探すわ", 22 | "menu": "メニュー", 23 | "admin_menu": "管理", 24 | "member": "メンバ", 25 | "use_stamp": "スタンプにする", 26 | "unuse_stamp": "スタンプをやめる", 27 | "unprivilege_user": "管理者権限を剥奪する", 28 | "privilege_user": "管理者にする", 29 | "unauthorize_user": "認可を取り消す", 30 | "authorize_user": "認可する", 31 | "delete_user": "削除する", 32 | "unauth_count": "未認証ユーザが%1人います", 33 | "response_count": "新着レスが%1個あります", 34 | "res_from": "%1さんからレスがありました", 35 | "res_body": "「%1」", 36 | "delete_button": "×", 37 | "user_info_update": "アプリケーション更新に伴い、ユーザ情報の再登録をします", 38 | "clearres": "新着レスクリア", 39 | "attach_photo": "写真を添付", 40 | "attach_stamp": "スタンプを投稿", 41 | "need_login": "Massrの利用には、ログインが必要です。", 42 | "photos": "写真", 43 | "user_photos": "%1の写真", 44 | "all_photos": "すべての写真", 45 | "stamps": "スタンプリスト", 46 | "liked": "わかられた", 47 | "likes": "わかった", 48 | "settings": "設定", 49 | "logout": "ログアウト", 50 | "unauthorized": "認証されるまでしばらくお待ち下さい", 51 | "basic_setting": "基本設定", 52 | "local_setting": "ローカル設定", 53 | "save": "保存", 54 | "registration": "登録", 55 | "display_name": "表示名", 56 | "icon": "アイコン", 57 | "upload_icon": "アイコンを変更", 58 | "icon_size_info": "(一辺45px〜90pxの正方形)", 59 | "use_twitter_icon": "現在のTwitterのアイコンを使用する", 60 | "icon_size_over": "画像サイズは90x90以内にしてください", 61 | "desktop_notice": "デスクトップ通知", 62 | "fail_like": "わかるわに失敗しました", 63 | "deny_delete": "削除は発言者本人にしかできません", 64 | "confirm_delete": "本当に削除してよろしいいですか?", 65 | "deny_cancel_admin": "管理者の認可は取り消せません", 66 | "deny_delete_admin": "管理者の削除はできません", 67 | "deny_nominate_admin": "未認可メンバは管理者指名できません", 68 | "success_change_status": "のステータスを変更しました", 69 | "success_use_stamp": "スタンプに登録しました", 70 | "success_unuse_stamp": "スタンプから削除しました", 71 | "success_delete_user": "を削除しました", 72 | "fail_change_status": "ステータス変更に失敗しました", 73 | "success_update_stamp_tag": "スタンプのタグを更新しました" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /plugins/media/twitter.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # plugins/media/twitter.rb : massr plugin of upload image to twitter 4 | # 5 | # usage: in default.json file: 6 | # 7 | # // if no *_secret , uses MEDIA_*_SECRET from ENV 8 | # "plugin": { 9 | # "media/twitter": { 10 | # "consumer_key": "hoge", 11 | # "consumer_secret": "fuga", 12 | # "access_token": "foo", 13 | # "access_token_secret", "bar" 14 | # } 15 | # } 16 | # 17 | # Copyright (C) 2014 by The wasam@s production 18 | # https://github.com/wasamas/massr 19 | # 20 | # Distributed under GPL 21 | # 22 | require 'twitter' 23 | require 'rmagick' 24 | 25 | # 26 | # Massr Twitter media plugin 27 | # 28 | module Massr 29 | module Plugin::Media 30 | class Twitter 31 | DEFAULT_UPLOAD_PHOTO_SIZE = 2048 32 | 33 | def initialize(label, opts) 34 | @conf = { 35 | consumer_key: opts['consumer_key'] || ENV['MEDIA_CONSUMER_KEY'], 36 | consumer_secret: opts['consumer_secret'] || ENV['MEDIA_CONSUMER_SECRET'], 37 | access_token: opts['access_token'] || ENV['MEDIA_ACCESS_TOKEN'], 38 | access_token_secret: opts['access_token_secret'] || ENV['MEDIA_ACCESS_TOKEN_SECRET'] 39 | } 40 | if @conf.values.include?(nil) 41 | raise StandardError::new('not specified OAuth keys') 42 | end 43 | @client ||= init_client 44 | end 45 | 46 | def resize_file(path, size=0, square=false) 47 | size = DEFAULT_UPLOAD_PHOTO_SIZE if size == 0 48 | photo = Magick::ImageList.new(path).first 49 | if photo.columns > size || photo.rows > size 50 | photo.resize_to_fit!(size,size) 51 | photo.write(path) 52 | end 53 | if square 54 | img = Magick::Image.new(size, size) 55 | img.background_color = '#ffffff' 56 | img.composite!(photo, Magick::CenterGravity, Magick::OverCompositeOp) 57 | img.format = photo.format 58 | img.write(path) 59 | img.destroy! 60 | end 61 | photo.destroy! 62 | end 63 | 64 | def upload_file(path, content_type, display_size = nil) 65 | retry_count = 0 66 | begin 67 | status = @client.update_with_media(Time.now.to_s, File.new(path)) 68 | return status.attrs[:entities][:media].first[:media_url] 69 | rescue 70 | init_client 71 | retry if (retry_count += 1) < 10 72 | raise 73 | end 74 | end 75 | 76 | private 77 | def init_client 78 | ::Twitter::REST::Client.new(@conf) 79 | end 80 | end 81 | end 82 | end 83 | 84 | -------------------------------------------------------------------------------- /views/index.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title 5 | -if defined?(q) && q 6 | Search "#{q}" / #{_site_name} 7 | - else 8 | #{_site_name} 9 | != haml :head 10 | %body 11 | != haml :header, locals: {q: q} 12 | .container-fluid 13 | - if defined?(q) && q 14 | .row-fluid 15 | .span9 16 | - exist_pin = Massr::SearchPin.find_by(word: q) 17 | Search " 18 | %span#query-string{title: q}<#{q.truncate} 19 | " 20 | %a.share-search#share-search{href: '#', class: exist_pin ? 'hide' : ''} 21 | %i.icon-share-alt.share-search{title: 'share search result'} 22 | %a.share-search#unshare-search{href: '#', class: exist_pin ? '' : 'hide'} 23 | %i.icon-remove.share-search{title: 'unshare search result'} 24 | .row-fluid 25 | .span9 26 | .main 27 | - user = Massr::User.find_by(id: session[:user_id]) 28 | - get_icon_url(user) 29 | - if user.status == 0 30 | - unauth_count = Massr::User.where(status: 9).count 31 | - unless unauth_count == 0 32 | .alert.alert-info 33 | %a{:href=>'/admin'}<#{_unauth_count(unauth_count)} 34 | != haml :notify 35 | - response_count = user.res_ids.size 36 | .alert.alert-info#new-res-size-main 37 | %a#new-res-notice-text{:href=>"/user/#{current_user.massr_id}/res"}<#{_response_count(response_count)} 38 | %a#new-res-notice-delete-button.close{:href => "#", :title => _clearres} "POST", :action => "/statement", :enctype => 'multipart/form-data'} 41 | %div 42 | %textarea#text-new{:type => "text", :name => "body", :tabindex => "2"} 43 | %input{csrf_input} 44 | %div.button 45 | %button.btn.btn-small.submit{:type => "submit", :tabindex => "3"}<#{_post} 46 | - if media_plugins.size != 0 47 | .photo-items 48 | %input.photo-shadow{:type => "file", :name => "photo", :accept => 'image/*', :tabindex => '-1'} 49 | %a.photo-button{:href => '#', :tabindex => "5"}< 50 | %i.icon-camera.photo-button{:title => _attach_photo} 51 | %img.photo-preview 52 | %span.photo-name 53 | .stamp-items{:id => 'submit-stamp'} 54 | %a.stamp-button{:href => '#stamps', :tabindex => "4"}< 55 | %i.icon-th.stamp-button{:title => _attach_stamp} 56 | != haml :statements ,:locals => {:res_ids => nil , :statements => statements,:q => q} 57 | .span3 58 | != haml :sidebar ,:locals => {:q => q} 59 | != haml :footer 60 | -------------------------------------------------------------------------------- /routes/search.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # routes/search.rb 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | module Massr 11 | class App < Sinatra::Base 12 | before '/search*' do 13 | @q = params[:q].strip if params[:q] 14 | redirect to('/') unless @q 15 | end 16 | 17 | get '/search.json' do 18 | [].tap {|a| 19 | Statement.get_statements(param_date, {body: /#{@q}/}).each do |statement| 20 | a << statement.to_hash 21 | end 22 | }.to_json 23 | end 24 | 25 | get '/search' do 26 | if @q.size == 0 then 27 | redirect to('/') 28 | return 29 | end 30 | 31 | if @q != params[:q] then 32 | redirect to('/search?q=' + @q) 33 | return 34 | end 35 | 36 | cached_statements = cache.get("search:#{@q}") 37 | if(cached_statements) 38 | statements = [] 39 | JSON.parse(cached_statements).each do |statement| 40 | statements << Statement.new(statement) 41 | end 42 | else 43 | statements = Statement.get_statements(param_date, {body: /#{@q}/i}) 44 | cache.set('query_list', (cache.get('query_list') || []).push(@q).uniq) 45 | cache.set("search:#{@q}", statements.to_json) 46 | end 47 | begin 48 | page = haml :index , :locals => { 49 | :statements => statements , 50 | :q => @q} 51 | rescue RegexpError 52 | redirect to('/') 53 | end 54 | end 55 | 56 | get '/search/pins.json' do 57 | [].tap{|h| 58 | SearchPin.all.each do |pin| 59 | h << {'q' => h[pin.word], 'label' => h[pin.label]} 60 | end 61 | }.to_json 62 | end 63 | 64 | post '/search/pin' do 65 | begin 66 | pin = SearchPin.create_by_word(params[:q], params[:label]) 67 | return [{'q' => pin.word, 'label' => pin.label}].to_json 68 | rescue MongoMapper::DocumentNotValid 69 | return 409 70 | end 71 | end 72 | 73 | delete '/search/pin' do 74 | begin 75 | pin = SearchPin.find_by(word: params[:q]) 76 | pin.destroy 77 | return [{'q' => pin.word, 'label' => pin.label}].to_json 78 | rescue NoMethodError 79 | return 404 80 | end 81 | end 82 | 83 | put '/search/pin' do 84 | begin 85 | pin = SearchPin::find_by(word: params[:q]) 86 | pin.label = params[:label] 87 | return [{'q' => pin.word, 'label' => pin.label}].to_json 88 | rescue NoMethodError 89 | return 404 90 | end 91 | end 92 | 93 | after '/search/pin' do 94 | if (not request.get?) 95 | clear_search_cache(params[:q]) 96 | end 97 | end 98 | end 99 | end 100 | 101 | 102 | -------------------------------------------------------------------------------- /helpers/resource.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # helpers/resource.rb : settings and resources 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | require 'open-uri' 12 | 13 | module Massr 14 | class App < Sinatra::Base 15 | custom_settings_uri = nil 16 | custom_settings = {} 17 | env = ENV['MASSR_SETTINGS'] 18 | 19 | if env # online 20 | if %r|\Ahttps?://| =~ env 21 | # saving copy to cache 22 | custom_settings_uri = '/custom_cache.json' 23 | open("public#{custom_settings_uri}", 'w'){|o|o.write(open(env, &:read))} 24 | else # local 25 | if %r|\.\.| =~ env 26 | puts "MASSR_SETTINGS cannot contains '..'." 27 | exit 28 | end 29 | custom_settings_uri = env 30 | custom_settings_uri = '/' + custom_settings_uri if %r|\A/| !~ custom_settings_uri 31 | end 32 | end 33 | default_settings = JSON.parse(open("public/default.json", &:read)) 34 | custom_settings = JSON.parse(open("public#{custom_settings_uri}", &:read)) if custom_settings_uri 35 | default_settings.keys.each do |key| 36 | default_settings[key].merge!(custom_settings[key]) if custom_settings[key] 37 | end 38 | SETTINGS = default_settings 39 | 40 | define_method(:massr_settings) do 41 | custom_settings_uri 42 | end 43 | 44 | PLUGINS = [] 45 | SETTINGS['plugin'].each do |plugin_name, opts| 46 | begin 47 | genre, klass, label = plugin_name.split(/[\/ ]/) 48 | require_relative "../plugins/#{genre}/#{klass}" 49 | PLUGINS << (Massr::Plugin.const_get(genre.capitalize)).const_get(klass.capitalize).new(label, opts) 50 | rescue LoadError 51 | puts "cannot load plugin: #{plugin_name}." 52 | rescue NameError 53 | puts "load plugin module not found: #{genre}/#{klass}" 54 | end 55 | end 56 | 57 | # needs a cache plugin instance 58 | unless PLUGINS.find{|plugin| plugin.class.to_s =~ /^Massr::Plugin::Cache::/} 59 | require_relative "../plugins/cache/memcached" 60 | PLUGINS << Massr::Plugin::Cache::Memcached.new('default') 61 | end 62 | 63 | # define methods to search plugin instance 64 | helpers do 65 | [:notify, :media, :cache].each do |genre| 66 | define_method("#{genre}_plugins") do 67 | PLUGINS.select do |plugin| 68 | /^Massr::Plugin::#{genre.to_s.capitalize}::/ =~ plugin.class.to_s 69 | end 70 | end 71 | 72 | define_method(genre) do 73 | __send__("#{genre}_plugins").first 74 | end 75 | end 76 | end 77 | 78 | SETTINGS['local'].each do |key, value| 79 | define_method("_#{key}"){|*args| 80 | value.gsub(/%(\d+)/){args[$1.to_i - 1]} 81 | } 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /views/user.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Massr - Mini Wassr - User Registration. 5 | != haml :head 6 | %body 7 | != haml :header 8 | .container-fluid 9 | .row-fluid 10 | .span9 11 | .main 12 | %p 13 | =session[:errors] 14 | - if update 15 | .info.alert-info 16 | #{_user_info_update} 17 | %h2<#{_basic_setting} 18 | %form.form-horizontal{:method => "POST", :action => '/user', :enctype => 'multipart/form-data'} 19 | %input{csrf_input} 20 | - user = Massr::User.find_by(id: session[:user_id]) if session[:user_id] 21 | .control-group 22 | .control-label 23 | - if user 24 | #{_icon} 25 | - else 26 | Twitter ID 27 | .controls 28 | - if user 29 | %img{:src => get_icon_url(user), :alt => user.twitter_id, :class => 'massr-icon'} 30 | - else 31 | =session[:twitter_id] 32 | - if media_plugins 33 | %input.photo-shadow.for-icon{:type => "file", :name => "newicon", :accept => 'image/*', :tabindex => '-1'} 34 | %img.photo-preview.massr-icon 35 | %span.photo-name 36 | %a.photo-button{:href => '#'} 37 | #{_upload_icon} 38 | #{_icon_size_info} 39 | %label.checkbox{:for => 'use-twitter-icon'} 40 | %input#use-twitter-icon{:type => 'checkbox', :name => 'use_twitter_icon', :value => '1'} 41 | #{_use_twitter_icon} 42 | .control-group 43 | %label.control-label{:for => 'input-massr_id'} "text", :id => "input-massr_id", :name => "massr_id", :value => user ? user.massr_id : session[:twitter_id]} 46 | .control-group 47 | %label.control-label{:for => 'input-name'}<#{_display_name} 48 | .controls 49 | %input{:type => "text", :id => 'input-name', :name => "name" ,:value => user ? user.name : session[:twitter_name]} 50 | .control-group 51 | %label.control-label{:for => 'input-email'} "text", :id => 'input-email', :name => "email" , :value => user ? user.email : ''} 54 | .control-group 55 | .controls 56 | - if user 57 | %button.btn.btn-large{:type => "submit"}<#{_save} 58 | - else 59 | %button.btn.btn-primary.btn-large{:type => "submit"}<#{_registration} 60 | %hr 61 | %h2<#{_local_setting} 62 | %form.form-horizontal 63 | .control-group 64 | .controls 65 | %label.checkbox 66 | %input{type: 'checkbox', id: 'popup-notification'} 67 | #{_desktop_notice} 68 | .span3 69 | - if user 70 | != haml :sidebar 71 | != haml :footer 72 | -------------------------------------------------------------------------------- /assets/js/plugins/notify/like.js: -------------------------------------------------------------------------------- 1 | /* 2 | * notify/like.js : notify like plugin for massr 3 | * 4 | * Copyright (C) 2015 by The wasam@s production 5 | * 6 | * Distributed under GPL 7 | */ 8 | 9 | $(function(){ 10 | var me = Massr.me; 11 | var _ = Massr.settings['local']; 12 | 13 | Massr.plugin_notify_like = function(id, opts){ 14 | var del = opts['delete'] || 'owner'; 15 | var myIcon = $('#'+id+' img[alt='+me+']').length !== 0; 16 | 17 | var likeButton = $('#'+id+'-like'); 18 | var unlikeButton = $('#'+id+'-unlike'); 19 | if(myIcon){ 20 | likeButton.hide(); 21 | }else{ 22 | unlikeButton.hide(); 23 | } 24 | 25 | likeButton.on('click', function(){ 26 | $.ajax({ 27 | url: '/plugin/notify/like/' + id, 28 | type: 'POST', 29 | dataType: 'json' 30 | }).done(function(result){ 31 | plugin_notify_like_draw_icons(id, result); 32 | }).fail(function(XMLHttpRequest, textStatus, errorThrown){ 33 | message.error('(' + textStatus + ')'); 34 | }); 35 | 36 | return false; 37 | }); 38 | 39 | unlikeButton.on('click', function(){ 40 | $.ajax({ 41 | url: '/plugin/notify/like/' + id + '/' + me, 42 | type: 'DELETE', 43 | dataType: 'json' 44 | }).done(function(result){ 45 | plugin_notify_like_draw_icons(id, result); 46 | }).fail(function(XMLHttpRequest, textStatus, errorThrown){ 47 | message.error('(' + textStatus + ')'); 48 | }); 49 | 50 | return false; 51 | }); 52 | 53 | $('#' + id).on('click', 'a.' + id + '-delete', function(){ 54 | var name = $('img', this).attr('alt'); 55 | 56 | $.ajax({ 57 | url: '/plugin/notify/like/' + id + '/' + name, 58 | type: 'DELETE', 59 | dataType: 'json' 60 | }).done(function(result){ 61 | plugin_notify_like_draw_icons(id, result); 62 | }).fail(function(XMLHttpRequest, textStatus, errorThrown){ 63 | message.error('(' + textStatus + ')'); 64 | }); 65 | 66 | return false; 67 | }); 68 | 69 | Massr.intervalFunctions.push(function(){ 70 | $.getJSON('/plugin/notify/like/' + id + '.json', function(json){ 71 | plugin_notify_like_draw_icons(id, json); 72 | }); 73 | }); 74 | 75 | function plugin_notify_like_draw_icons(id, icons){ 76 | var elem = $('#' + id); 77 | 78 | elem.empty(); 79 | $('#'+id+'-like').show(); 80 | $('#'+id+'-unlike').hide(); 81 | $.each(icons, function(name, val){ 82 | if(del == 'any'){ 83 | elem.append( 84 | $('').addClass(id + '-delete').attr('href', '#').append( 85 | $(''). 86 | addClass('massr-icon-mini'). 87 | attr('src', val[1]). 88 | attr('alt', name). 89 | attr('title', 'delete ' + name) 90 | ) 91 | ).append(' ') 92 | }else{ 93 | elem.append( 94 | $(''). 95 | addClass('massr-icon-mini'). 96 | attr('src', val[1]). 97 | attr('alt', name). 98 | attr('title', name) 99 | ) 100 | } 101 | if(me == name){ 102 | $('#'+id+'-like').hide(); 103 | $('#'+id+'-unlike').show(); 104 | } 105 | }); 106 | } 107 | } 108 | }); 109 | -------------------------------------------------------------------------------- /massr.rb: -------------------------------------------------------------------------------- 1 | # massr.rb : Massr - Mini Wassr 2 | # 3 | # Copyright (C) 2012 by The wasam@s production 4 | # https://github.com/wasamas/massr 5 | # 6 | # Distributed under GPL 7 | 8 | Bundler.require(:default, ENV['RACK_ENV'] || :development) 9 | require 'json' 10 | 11 | require_relative 'plugins/logging' 12 | require_relative 'plugins/async_request' 13 | 14 | module Massr 15 | # definition of module of plugins 16 | module Plugin 17 | module Notify; end 18 | module Media; end 19 | module Cache; end 20 | end 21 | 22 | class App < Sinatra::Base 23 | set :haml, {format: :html5} 24 | 25 | set :assets_precompile, %w(application.js application.css *.png *.jpg *.svg) 26 | set :assets_css_compressor, :yui 27 | set :assets_js_compressor, :uglifier 28 | set :assets_paths, %w(assets/js assets/css) 29 | register Sinatra::AssetPipeline 30 | RailsAssets.load_paths.each do |path| 31 | settings.sprockets.append_path(path) 32 | end 33 | 34 | configure :production do 35 | require 'newrelic_rpm' if ENV['NEW_RELIC_LICENSE_KEY'] 36 | 37 | Mail.defaults do # using sendgrid plugin 38 | delivery_method :smtp, { 39 | :address => 'smtp.sendgrid.net', 40 | :port => ENV['SENDGRID_PORT'] || 587, 41 | :domain => 'sendgrid.net', 42 | :user_name => ENV['SENDGRID_USERNAME'], 43 | :password => ENV['SENDGRID_PASSWORD'], 44 | :authentication => :plain, 45 | :enable_starttls_auto => true 46 | } 47 | end 48 | 49 | enable :logging 50 | Massr::Plugin::Logging.instance.level(Massr::Plugin::Logging::WARN) 51 | end 52 | 53 | configure :development, :test do 54 | # loading TWITTER_CONSUMER_ID and TWITTER_CONSUMER_SECRET, 55 | # GMAIL_USERNAME and GMAIL_PASSWORD 56 | Dotenv.load 57 | 58 | register Sinatra::Reloader 59 | also_reload './*.rb' 60 | also_reload './models/*.rb' 61 | also_reload './helpers/*.rb' 62 | 63 | disable :protection 64 | 65 | Mail.defaults do # using sendgrid plugin 66 | delivery_method :smtp, { 67 | address: 'smtp.gmail.com', 68 | port: '587', 69 | user_name: ENV['GMAIL_USERNAME'], 70 | password: ENV['GMAIL_PASSWORD'], 71 | :authentication => :plain, 72 | :enable_starttls_auto => true 73 | } 74 | end 75 | 76 | enable :logging 77 | Massr::Plugin::Logging.instance.level(Massr::Plugin::Logging::DEBUG) 78 | end 79 | 80 | Mongoid::load!('config/mongoid.yml') 81 | Mongoid.raise_not_found_error = false 82 | 83 | session_expire = 60 * 60 * 24 * 30 - 1 84 | use Rack::Session::Dalli, cache: Dalli::Client.new, expire_after: session_expire 85 | 86 | OmniAuth.config.full_host = ENV['FULL_HOST'] if ENV['FULL_HOST'] 87 | twitter_id = ENV['TWITTER_CONSUMER_ID'] 88 | twitter_secret = ENV['TWITTER_CONSUMER_SECRET'] 89 | use(OmniAuth::Strategies::Twitter, twitter_id, twitter_secret) 90 | 91 | use Rack::Csrf 92 | 93 | # max entries of 1st view 94 | $limit = 20 95 | end 96 | end 97 | 98 | require_relative 'models/init' 99 | require_relative 'helpers/init' 100 | require_relative 'routes/init' 101 | 102 | Massr::App::run! if __FILE__ == $0 103 | -------------------------------------------------------------------------------- /models/user.rb: -------------------------------------------------------------------------------- 1 | require_relative 'statement' 2 | 3 | module Massr 4 | class User 5 | include ::Mongoid::Document 6 | include ::Mongoid::Timestamps 7 | store_in collection: "massr.users" 8 | 9 | # user status code 10 | ADMIN = 0 11 | AUTHORIZED = 1 12 | UNAUTHORIZED = 9 13 | 14 | field :massr_id, type: String 15 | field :twitter_user_id, type: String 16 | field :twitter_id, type: String 17 | field :twitter_icon_url, type: String 18 | field :twitter_icon_url_https, type: String 19 | field :name, type: String 20 | field :email, type: String 21 | field :status, type: Integer, default: UNAUTHORIZED 22 | field :res_ids, type: Array 23 | validates_presence_of :massr_id, :twitter_user_id, :twitter_id, 24 | :twitter_icon_url, :twitter_icon_url, :name 25 | validates_uniqueness_of :massr_id, :twitter_user_id, :twitter_id 26 | 27 | has_many :statements, class_name: 'Massr::Statement' 28 | 29 | def self.create_by_registration_form(request) 30 | user = User.new(massr_id: request[:massr_id]) 31 | user.update_profile(request) 32 | return user 33 | end 34 | 35 | def self.change_status(id, status) 36 | user = User.find_by(massr_id: id) 37 | case status.to_s 38 | when ADMIN.to_s then 39 | user[:status] = ADMIN 40 | when AUTHORIZED.to_s then 41 | user[:status] = AUTHORIZED 42 | when UNAUTHORIZED.to_s then 43 | user[:status] = UNAUTHORIZED 44 | else 45 | return 46 | end 47 | user.save 48 | end 49 | 50 | def self.each_authorized_user_without(me) 51 | where( massr_id: {:$ne => me.massr_id}, 52 | status: {:$ne => Massr::User::UNAUTHORIZED}). 53 | order_by(updated_at: 'desc').each do |member| 54 | yield member 55 | end 56 | end 57 | 58 | def update_profile(request) 59 | self.twitter_user_id = request[:twitter_user_id] 60 | self.twitter_id = request[:twitter_id] 61 | self.twitter_icon_url = request[:twitter_icon_url] 62 | self.twitter_icon_url_https = request[:twitter_icon_url_https] 63 | self.name = request[:name] 64 | self.email = request[:email] 65 | 66 | # 最初期のユーザは管理者として登録 67 | self.status = ADMIN if User.count == 0 68 | 69 | self.save! 70 | return self 71 | end 72 | 73 | def res_ids 74 | self[:res_ids] = [] unless self[:res_ids] 75 | self[:res_ids] 76 | end 77 | 78 | def clear_res_ids 79 | self[:res_ids] = [] 80 | save! 81 | return self 82 | end 83 | 84 | def admin? 85 | status == ADMIN 86 | end 87 | 88 | def authorized? 89 | status != UNAUTHORIZED 90 | end 91 | 92 | def to_json(stat = nil) 93 | to_hash.to_json 94 | end 95 | 96 | def to_hash 97 | { 98 | 'id' => id.to_s, 99 | 'massr_id' => massr_id, 100 | 'twitter_user_id' => twitter_user_id, 101 | 'twitter_id' => twitter_id, 102 | 'twitter_icon_url' => twitter_icon_url, 103 | 'twitter_icon_url_https' => twitter_icon_url_https, 104 | 'name' => name, 105 | 'email' => email, 106 | 'status' => status, 107 | } 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /plugins/notify/like.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # plugins/notify/like.rb : massr notify plugin for global like 4 | # 5 | # Copyright (C) 2013 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | # Usage: 11 | # "plugin": { 12 | # "notify/like party": { 13 | # "label": "Do you attend the party?", 14 | # "delete": "any" // "any" or "owner" 15 | # } 16 | # } 17 | # 18 | module Massr 19 | module Plugin::Notify 20 | class Like 21 | SETTINGS = {} unless const_defined? :SETTINGS 22 | 23 | attr_reader :label, :delete 24 | 25 | def self.likes(id) 26 | JSON.parse(Massr::PluginSetting.get(id) || '{}') 27 | end 28 | 29 | def self.update(id, likes) 30 | Massr::PluginSetting.set(id, likes.to_json) 31 | end 32 | 33 | def self.cancel(id, name) 34 | tmp = likes(id) 35 | val = tmp.delete(name) 36 | update(id, tmp) 37 | return val 38 | end 39 | 40 | def self.add(id, me) 41 | tmp = likes(id) 42 | tmp[me.massr_id] = [me.twitter_icon_url, me.twitter_icon_url_https] 43 | update(id, tmp) 44 | end 45 | 46 | def self.delete(id, name, me) 47 | case SETTINGS[id].delete 48 | when 'owner' 49 | if name == me.massr_id 50 | return cancel(id, name) 51 | else 52 | return nil 53 | end 54 | when 'any' 55 | return cancel(id, name) 56 | end 57 | return nil 58 | end 59 | 60 | def self.to_json(id) 61 | likes(id).to_json 62 | end 63 | 64 | def initialize(plugin_id, opts) 65 | @id = plugin_id 66 | SETTINGS[@id] = self 67 | 68 | @label = opts['label'] || '' 69 | @delete = opts['delete'] || 'owner' 70 | end 71 | 72 | def render 73 | html = "#{@label} " 74 | html << %Q|| 75 | html << %Q|| 76 | html << %Q|#{icons}| 77 | end 78 | 79 | private 80 | 81 | def icons 82 | self.class.likes(@id).map{|user, (http, https)| 83 | case @delete 84 | when "any" 85 | %Q|#{user} | 86 | else # "owner" 87 | %Q|#{user}| 88 | end 89 | }.join 90 | end 91 | end 92 | end 93 | 94 | class App < Sinatra::Base 95 | get '/plugin/notify/like/:plugin.json' do 96 | Massr::Plugin::Notify::Like.to_json(params[:plugin]) 97 | end 98 | 99 | post '/plugin/notify/like/:plugin' do 100 | me = User.find_by(id: session[:user_id]) 101 | Massr::Plugin::Notify::Like.add(params[:plugin], me) 102 | Massr::Plugin::Notify::Like.to_json(params[:plugin]) 103 | end 104 | 105 | delete '/plugin/notify/like/:plugin/:user' do 106 | me = User.find_by(id: session[:user_id]) 107 | Massr::Plugin::Notify::Like.delete(params[:plugin], params[:user], me) 108 | Massr::Plugin::Notify::Like.to_json(params[:plugin]) 109 | end 110 | end 111 | end 112 | 113 | -------------------------------------------------------------------------------- /models/statement.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | 3 | module Massr 4 | class Statement 5 | include ::Mongoid::Document 6 | include ::Mongoid::Timestamps 7 | store_in collection: "massr.statements" 8 | 9 | field :body, type: String 10 | field :stamp, type: String 11 | field :photos, type: Array 12 | #field :ref_ids, type: Array # do not access directly, use refs field instead 13 | 14 | belongs_to :user, class_name: 'Massr::User', inverse_of: :statements 15 | embeds_many :likes, class_name: 'Massr::Like' 16 | 17 | has_many :refs, class_name: 'Massr::Statement', inverse_of: :res 18 | belongs_to :res, class_name: 'Massr::Statement', inverse_of: :refs 19 | 20 | has_one :stamp_source, class_name: 'Massr::Stamp', inverse_of: :original 21 | 22 | def custom_validation 23 | if body.nil && stamp.nil 24 | errors.add( :body, "Please enter the body or stamp.") 25 | errors.add( :stamp, "Please enter the body or stamp.") 26 | end 27 | end 28 | 29 | def self.get_statements(date, queries={}) 30 | queries[:created_at.lt] = Time.parse(date) 31 | return self.where(queries).order_by(created_at: 'desc').limit($limit) 32 | end 33 | 34 | def self.add_photo(id, uri) 35 | statement = Statement.find_by(id: id) 36 | statement.push(photos: uri.to_s) 37 | statement.save!(validate: false) 38 | end 39 | 40 | def self.delete_all_statements(user) 41 | Statement.destroy_all(user_id: user.id) 42 | self.where('likes.user_id': user.id).each do |statement| 43 | statement.likes.delete_if{|like| like.user_id == user.id} 44 | statement.save!(validate: false) 45 | end 46 | end 47 | 48 | def update_statement(request) 49 | self.user, self.body, self.photos, self.stamp = request[:user], request[:body], request[:photos], request[:stamp] 50 | 51 | if request[:res_id] 52 | res_statement = Statement.find_by(id: request[:res_id]) 53 | self.res = res_statement 54 | 55 | if res_statement.user.massr_id != user.massr_id 56 | res_statement.user.push(res_ids: self.id) 57 | res_statement.user.save!(validate: false) 58 | end 59 | end 60 | 61 | if save(validate: false) 62 | # aync add photos in body message 63 | re = URI.regexp(['http', 'https']) 64 | request_uri = URI.parse(request.url) 65 | self.body.scan(re) do 66 | uri = URI.parse($&) rescue next 67 | next if uri.host == request_uri.host 68 | response = nil 69 | Massr::Plugin::AsyncRequest.new(uri).future.add_photo(self.id) 70 | end unless self.body.nil? 71 | end 72 | 73 | return self 74 | end 75 | 76 | def photos # for compatibility 77 | self[:photos] || [] 78 | end 79 | 80 | def add_like(like) 81 | self.likes << like 82 | save!(validate: false) 83 | end 84 | 85 | def like?(user) 86 | likes.map{|like| like.user.id == user.id}.include?(true) 87 | end 88 | 89 | def to_hash 90 | { 91 | 'id' => id.to_s, 92 | 'created_at' => created_at.localtime.strftime('%Y-%m-%d %H:%M:%S'), 93 | 'body' => body, 94 | 'user' => user.to_hash, 95 | 'likes' => likes.map{|l| l.to_hash}, 96 | 'ref_ids' => ref_ids, 97 | 'res' => res ? res.to_hash : nil, 98 | 'photos' => photos, 99 | 'stamp' => stamp 100 | } 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /routes/statement.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # routes/statement.rb 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | 11 | module Massr 12 | class App < Sinatra::Base 13 | post '/statement.?:format?' do 14 | @statement = Statement.new 15 | 16 | request[:user] = User.find_by(id: session[:user_id]) 17 | begin 18 | (request[:photos] ||= []) << media_upload(params[:photo], SETTINGS['setting']['upload_photo_size']) 19 | rescue Massr::NoPhotoError 20 | # no photos 21 | end 22 | 23 | if (request[:stamp].nil?) 24 | @statement.update_statement( request ) unless request[:body].size == 0 25 | else 26 | @statement.update_statement( request ) unless request[:stamp].size == 0 27 | stamp = Stamp.find_by(id: request[:stamp_id]) 28 | stamp.post_stamp() unless stamp.nil? 29 | cache.delete('stamp') 30 | cache.set('stamp', Stamp.get_stamps.map{|i| i.to_hash}) 31 | end 32 | if @statement.res && @statement.res.user.email.length > 0 && @statement.res.user.massr_id != request[:user].massr_id 33 | send_mail(@statement.res.user, @statement) 34 | end 35 | 36 | if params[:format] == 'json' 37 | @statement.to_hash.to_json 38 | else 39 | redirect to('/') 40 | end 41 | end 42 | 43 | before '/statement/:id.?:format?' do 44 | case params[:id] 45 | when 'photos' 46 | @query = {"photos" => {:$ne => [] } } 47 | else 48 | @statement = Statement.find_by(id: params[:id]) 49 | not_found unless @statement 50 | end 51 | end 52 | 53 | after '/statement*' do 54 | unless request.get? 55 | clear_search_cache(@statement.body) rescue nil 56 | end 57 | end 58 | 59 | 60 | get '/statement/photos' do 61 | haml :user_photos, :locals => { 62 | :statements => Statement.get_statements(param_date, @query), 63 | :q => nil, 64 | :pagenation => true} 65 | end 66 | 67 | get '/statement/photos.json' do 68 | [].tap {|a| 69 | Statement.get_statements(param_date, @query).each do |statement| 70 | a << statement.to_hash 71 | end 72 | }.to_json 73 | end 74 | 75 | get '/statement/:id.json' do 76 | @statement.to_hash.to_json 77 | end 78 | 79 | get '/statement/:id' do 80 | haml :user_statement, :locals => {:statement => @statement} 81 | end 82 | 83 | delete '/statement/:id' do 84 | if current_user == Statement.find_by(id: params[:id]).user 85 | stamp = Stamp.find_by("original.id": params[:id]) 86 | stamp.destroy if stamp 87 | Statement.find_by(_id: params[:id]).delete 88 | end 89 | redirect to('/') 90 | end 91 | 92 | before '/statement/:id/like' do 93 | @user = User.find_by(id: session[:user_id]) 94 | @statement = Statement.find_by(id: params[:id]) 95 | end 96 | 97 | post '/statement/:id/like' do 98 | @statement.likes.delete_if{|like| !like.user} 99 | unless @statement.like?(@user) 100 | @statement.add_like(Like.new(user: @user)) 101 | end 102 | @statement.to_hash.to_json 103 | end 104 | 105 | delete '/statement/:id/like' do 106 | @statement.likes.delete_if{ |like| !like.user} 107 | @statement.likes.delete_if{ |like| like.user.id == @user._id} 108 | @statement.save!(validate: false) 109 | @statement.to_hash.to_json 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /helpers/init.rb: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; -*- 2 | # 3 | # helpers/init.rb : initialize helper 4 | # 5 | # Copyright (C) 2012 by The wasam@s production 6 | # https://github.com/wasamas/massr 7 | # 8 | # Distributed under GPL 9 | # 10 | require 'mail' 11 | 12 | module Massr 13 | class App < Sinatra::Base 14 | helpers do 15 | 16 | def csrf_meta 17 | {:name => "_csrf", :content => Rack::Csrf.token(env)} 18 | end 19 | 20 | def csrf_input 21 | {:type => 'hidden', :name => '_csrf', :value => Rack::Csrf.token(env)} 22 | end 23 | 24 | def current_user 25 | @current_user ||= User.find_by(id: session[:user_id]) 26 | end 27 | 28 | def send_mail(user, statement) 29 | 30 | body = statement.body ? statement.body : "" 31 | msg = <<-MAIL 32 | #{_res_from(statement.user.name)}: 33 | 34 | #{_res_body(body.strip)} 35 | MAIL 36 | 37 | Thread.start do 38 | begin 39 | Mail.deliver do 40 | from 'no-reply@wasamas.net' 41 | to user.email 42 | subject 'Message from Massr' 43 | content_type 'text/plain; charset=UTF-8' 44 | body msg.gsub(/^\t+/, '') 45 | end 46 | puts "sending mail to #{user.massr_id} successfully." 47 | rescue => e 48 | puts e.to_s 49 | end 50 | end 51 | end 52 | 53 | def random_masao 54 | "/img/masao#{['', '2', '3'].sample}.jpg" 55 | end 56 | 57 | def param_date 58 | date = params[:date] ? params[:date] : (Time.now + 10).strftime("%Y%m%d%H%M%S") 59 | end 60 | 61 | def get_icon_url(user) 62 | user['twitter_icon_url_https'] 63 | end 64 | 65 | def icon_dir 66 | SETTINGS['resource']['icon_dir'] || 'default' 67 | end 68 | 69 | def image_size_change(url, size, centering) 70 | begin 71 | host = url.match(%r|\Ahttps?://(.*?)/|)[1] 72 | rescue NoMethodError 73 | puts "Fatal error in image_size_change cause by dirty cache" 74 | end 75 | 76 | if (host =~ /\A[0-9a-zA-Z]+\.googleusercontent\.com\z/) 77 | pattern = /\/([whs][0-9]+|r(90|180|270)|-|c|p|o|d)+\// 78 | if url =~ pattern 79 | if centering 80 | url.sub(pattern , "/s#{size}-c/") 81 | else 82 | url.sub(pattern , "/s#{size}/") 83 | end 84 | else 85 | if centering 86 | url.split('/').insert(-2,"s#{size}-c").join('/') 87 | else 88 | url.split('/').insert(-2,"s#{size}").join('/') 89 | end 90 | end 91 | elsif host =~ /^(i|thumb)\.gyazo\.com$/ 92 | url.sub(%r|/thumb/\d+/|, "/thumb/#{size}/") 93 | else 94 | url 95 | end 96 | end 97 | 98 | def stamps 99 | cache.get('stamp') 100 | end 101 | 102 | def get_stamp(photo) 103 | dst = image_size_change(photo,1,false) 104 | Massr::Stamp.find_by(image_url: dst) 105 | end 106 | 107 | def clear_search_cache(body) 108 | new_query_list = (cache.get('query_list') || []).reject{|query| 109 | if /#{query}/i =~ body 110 | cache.delete("search:#{query}") 111 | true 112 | else 113 | false 114 | end 115 | } 116 | cache.set('query_list', new_query_list) 117 | end 118 | end 119 | end 120 | end 121 | 122 | class String 123 | def truncate(len = 20) 124 | matched = self.gsub( /\n/, ' ' ).scan( /^.{0,#{len - 3}}/u )[0] 125 | ($'.nil? || $'.empty?) ? matched : matched + '...' 126 | end 127 | end 128 | 129 | require_relative 'resource' 130 | require_relative 'media' 131 | require_relative 'stats' 132 | -------------------------------------------------------------------------------- /views/statement.haml: -------------------------------------------------------------------------------- 1 | .statement{:class => "id-st-#{statement._id}" + (is_new ? ' statement-new' : '')} 2 | 3 | / アイコン 4 | .statement-icon 5 | %a{:href => "/user/#{statement.user.massr_id}"} 6 | %img.massr-icon{:src => get_icon_url(statement.user)} 7 | 8 | %div{:class => "#{session[:user_id] == statement.user._id ? "statement-body-me statement-body" : "statement-body"}"} 9 | / レスポンス元発言 10 | - if statement.res 11 | .statement-res-icon 12 | %a{:href => "/user/#{statement.res.user.massr_id}"} 13 | %img.massr-icon-mini{:src => get_icon_url(statement.res.user), :alt => statement.res.user.name, :title => statement.res.user.name} 14 | .statement-res 15 | %a{:href => "/statement/#{statement.res._id}"} 16 | < #{statement.res.body} 17 | - unless statement.res.stamp.nil? 18 | %img{:src => "#{image_size_change(statement.res.stamp,16,true)}", :alt => "#{statement.res.stamp}", :title => "#{statement.res.stamp}"} 19 | 20 | / 発言本文 21 | - unless statement.body.nil? 22 | .statement-message< 23 | = statement.body.gsub(/[\r\n]+/, "\r") 24 | - unless statement.stamp.nil? 25 | .statement-stamp 26 | .stamp-style 27 | .stamp 28 | %img.statement-stamp-img{:src => "#{image_size_change(statement.stamp,SETTINGS['setting']['stamp_size'],true)}", :alt => "#{statement.stamp}", :title => "#{statement.stamp}"} 29 | 30 | / 画像表示 31 | .statement-photos 32 | - statement.photos.each do |photo| 33 | %a{:href => "##{statement._id}" ,:class => "popup-image"}< 34 | %img.statement-photo{:src => "#{photo}" , :alt => "#{statement._id}" , :title => "#{statement._id}"} 35 | != haml :popup_photo , :locals => {:statement => statement,:photo => photo, :detail => false} 36 | 37 | / 発言の付属情報 38 | .statement-info 39 | by 40 | %a{:href => "/user/#{statement.user.massr_id}"}<#{statement.user.name} 41 | at 42 | %a{:href => "/statement/#{statement._id}"} 43 | = statement.created_at.localtime.strftime('%Y-%m-%d %H:%M:%S') 44 | - statement.likes.delete_if{|like| !like.user} 45 | 46 | / アクション 47 | .statement-action 48 | - if statement.user._id == session[:user_id] 49 | %a.trash{:href => "#"}< 50 | %i.icon-trash{:title => _delete} 51 | .stamp-items 52 | %a.stamp-button{:href => "#stamps"}< 53 | %i.icon-th.stamp-button{:title => _attach_stamp} 54 | %a.res{:href => '#'}< 55 | %i.icon-comment{:title => _res} 56 | = statement.refs.size > 0 ? "(#{statement.refs.size})" : '' 57 | - like = statement.like?(current_user) ? 'unlike' : 'like' 58 | %a{:class => "id-like-#{statement._id} like-button #{like}", :href => '#'} 59 | %img.unlike{:src => "/img/wakaruwa.png", :alt => _unlike, :title => _unlike} 60 | %img.like{:src => "/img/wakaranaiwa.png", :alt => _like, :title => _like} 61 | 62 | / わかるわ一覧 63 | - unless statement.likes.size == 0 64 | .statement-like 65 | #{_like}: 66 | - statement.likes.each do |like| 67 | %a{:href => "/user/#{like.user.massr_id}"} 68 | %img.massr-icon-mini{:src => get_icon_url(like.user), :title => like.user.name, :alt => like.user.name} 69 | 70 | / レスフォーム 71 | .response{:class=>"id-res-#{statement._id}"} 72 | %form{:method => "POST", :action => "/statement", :enctype => 'multipart/form-data', :class=>"res-form"} 73 | %div 74 | %textarea{:type => "text", :name => "body"} 75 | %input{:type => "hidden", :name => "res_id", :value => statement._id} 76 | %input{csrf_input} 77 | %div.button 78 | %button.btn.btn-small.submit{:type => "submit"}<#{_post_res} 79 | - if media_plugins.size != 0 80 | .photo-items 81 | %input.photo-shadow{:type => "file", :name => "photo", :accept => 'image/*', :tabindex => '-1'} 82 | %a.photo-button{:href => '#'} 83 | %i.icon-camera.photo-button{:title => _attach_photo} 84 | %img.photo-preview 85 | %span.photo-name 86 | -------------------------------------------------------------------------------- /assets/js/jquery.auto-link.js: -------------------------------------------------------------------------------- 1 | /* 2 | * auto-link.js : make url to link automatic 3 | * 4 | * Copyright (C) 2015 by The wasam@s production 5 | * 6 | * Distributed under GPL 7 | */ 8 | 9 | $(function(){ 10 | function divID() { 11 | var result = "", i, random; 12 | for (i = 0; i < 32; i++) { 13 | random = Math.random() * 16 | 0; 14 | result += (i == 12 ? 4 : (i == 16 ? (random & 3 | 8) : random)).toString(16); 15 | } 16 | return result; 17 | } 18 | 19 | // load twitter widget 20 | window.twttr = (function(d, s, id) { 21 | var js, fjs = d.getElementsByTagName(s)[0], 22 | t = window.twttr || {}; 23 | if (d.getElementById(id)) return t; 24 | js = d.createElement(s); 25 | js.id = id; 26 | js.src = "https://platform.twitter.com/widgets.js"; 27 | fjs.parentNode.insertBefore(js, fjs); 28 | 29 | t._e = []; 30 | t.ready = function(f) { 31 | t._e.push(f); 32 | }; 33 | 34 | return t; 35 | }(document, "script", "twitter-wjs")); 36 | 37 | $.fn.autoLink = function(config){ 38 | var site = document.location.href.match(/(https?:\/\/[^\/]+\/).*/)[1]; 39 | this.each(function(){ 40 | var re = /((https?|ftp):\/\/[\(\)%#!\/0-9a-zA-Z_$@.&+-,'"*=;?:~-]+|^#[^#\s]+|\s#[^#\s]+)/g; 41 | var embed = ""; 42 | $(this).html( 43 | $(this).html().replace(re, function(u){ 44 | try { 45 | if (u.match(/^\s*#/)) { 46 | var array = u.split('#'); 47 | var prefix = array[0]; 48 | var tag = '#' + array[1]; 49 | return prefix + ''+tag+''; 50 | } else { 51 | var url = $.url(u); 52 | if (u.match(/https?:\/\/(mobile\.)?twitter\.com\/.+\/status\/(\d+)$/)) { 53 | // embed tweet 54 | var tweet_id = RegExp.$2; 55 | var s_id = divID(); 56 | embed += '
'; 57 | twttr.ready(function() { 58 | twttr.events.bind('rendered', function(ev) { 59 | $(ev.target).css({'width': 'auto'}); 60 | $(ev.target).contents().find('blockquote').css({'font-size':'85%'}); 61 | }); 62 | twttr.widgets.createTweet(tweet_id, $('#tw'+tweet_id+'-'+s_id).get(0),{cards:'hidden'}); 63 | }); 64 | } 65 | else if (u.match(/https?:\/\/www\.youtube\.com\/watch\?v=([0-9a-zA-Z_]+)/)) { 66 | embed += '