├── lib ├── tasks │ └── .gitkeep ├── assets │ └── .gitkeep ├── fbauth.rb ├── prop_extend.rb ├── ghauth.rb ├── backup_pages.rb ├── paperclip_ext.rb ├── evernote_auth.rb ├── block_profiler.rb ├── ar_async.rb └── backup_dropbox.rb ├── test ├── unit │ ├── .gitkeep │ ├── helpers │ │ ├── fbauth_helper_test.rb │ │ ├── ghauth_helper_test.rb │ │ ├── pages_helper_test.rb │ │ ├── dashboard_helper_test.rb │ │ └── pages │ │ │ └── members_helper_test.rb │ ├── page_test.rb │ ├── user_test.rb │ ├── fb_user_test.rb │ ├── gh_user_test.rb │ ├── page_user_test.rb │ └── page_history_test.rb ├── fixtures │ ├── .gitkeep │ ├── users.yml │ ├── page_taggings.yml │ ├── page_dates.yml │ ├── page_histories.yml │ ├── page_tags.yml │ ├── dropbox_users.yml │ ├── pages.yml │ ├── fb_users.yml │ ├── gh_users.yml │ ├── user_messages.yml │ └── page_users.yml ├── functional │ └── .gitkeep ├── integration │ └── .gitkeep ├── helpers │ ├── app_helper_test.rb │ ├── top_helper_test.rb │ ├── stats_helper_test.rb │ ├── sessions_helper_test.rb │ ├── settings_helper_test.rb │ ├── dropbox_auth_helper_test.rb │ └── user_messages_helper_test.rb ├── models │ ├── page_tag_test.rb │ ├── page_date_test.rb │ ├── dropbox_user_test.rb │ ├── page_tagging_test.rb │ └── user_message_test.rb ├── controllers │ ├── app_controller_test.rb │ ├── top_controller_test.rb │ ├── stats_controller_test.rb │ ├── sessions_controller_test.rb │ ├── dropbox_auth_controller_test.rb │ ├── user_messages_controller_test.rb │ └── settings_controller_test.rb ├── performance │ └── browsing_test.rb └── test_helper.rb ├── app ├── mailers │ └── .gitkeep ├── helpers │ ├── app_helper.rb │ ├── helps_helper.rb │ ├── pages_helper.rb │ ├── stats_helper.rb │ ├── exports_helper.rb │ ├── fbauth_helper.rb │ ├── ghauth_helper.rb │ ├── calendar_helper.rb │ ├── feedbacks_helper.rb │ ├── messages_helper.rb │ ├── sessions_helper.rb │ ├── settings_helper.rb │ ├── dropbox_auth_helper.rb │ ├── evernote_auth_helper.rb │ ├── user_messages_helper.rb │ ├── pages │ │ └── members_helper.rb │ └── application_helper.rb ├── assets │ ├── javascripts │ │ ├── preview.js.coffee │ │ ├── app │ │ │ ├── splash.js.coffee │ │ │ ├── page_search_panel.js.coffee │ │ │ ├── panel.js.coffee │ │ │ ├── markdown_toolbar.js.coffee │ │ │ └── settings.js.coffee │ │ ├── messages.js.coffee │ │ ├── shared │ │ │ ├── defer.js.coffee │ │ │ ├── labeled_button.js.coffee │ │ │ ├── app_cache.js.coffee │ │ │ ├── localstorage.js │ │ │ ├── modal_dialog.js.coffee │ │ │ ├── feedback.js.coffee │ │ │ └── utils.js.coffee │ │ └── models │ │ │ └── page_collection.js.coffee │ └── stylesheets │ │ ├── settings.scss │ │ ├── exports.css.less │ │ ├── feedbacks.css.less │ │ ├── evernote_auth.css.less │ │ ├── messages.css.scss │ │ ├── app │ │ ├── settings.scss │ │ ├── user_messages.scss │ │ ├── navigator.scss │ │ └── preview.css.scss │ │ ├── application.css │ │ ├── layout.scss │ │ ├── pages.scss │ │ └── toggle.css.scss ├── models │ ├── dropbox_user.rb │ ├── page_history.rb │ ├── page_date.rb │ ├── page_user.rb │ ├── settings.rb │ ├── page_tagging.rb │ ├── page_property.rb │ ├── user_property.rb │ ├── help.rb │ ├── page_tag.rb │ ├── feedback.rb │ ├── user_message.rb │ ├── gh_user.rb │ ├── fb_user.rb │ ├── user.rb │ └── evernote_user.rb ├── controllers │ ├── app_controller.rb │ ├── user_messages_controller.rb │ ├── exports_controller.rb │ ├── helps_controller.rb │ ├── feedbacks_controller.rb │ ├── fbauth_controller.rb │ ├── dropbox_auth_controller.rb │ ├── stats_controller.rb │ ├── ghauth_controller.rb │ ├── settings_controller.rb │ ├── evernote_auth_controller.rb │ ├── pages │ │ └── members_controller.rb │ ├── application_controller.rb │ ├── sessions_controller.rb │ ├── calendar_controller.rb │ └── messages_controller.rb └── views │ ├── sessions │ ├── test.html.erb │ └── destroy.html.erb │ ├── shared │ ├── _help.html.erb │ ├── _top_nav.html.erb │ ├── _dialog.html.erb │ └── _settings.html.erb │ ├── layouts │ └── application.html.erb │ ├── dropbox_auth │ └── callback.html.erb │ ├── feedbacks │ └── index.html.erb │ ├── app │ ├── _markdown_toolbar.html.erb │ └── _edit_help.html.erb │ ├── pages │ └── show.html.erb │ └── evernote_auth │ └── callback.html.erb ├── vendor ├── plugins │ └── .gitkeep └── assets │ ├── javascripts │ ├── .gitkeep │ └── shared │ │ ├── html2canvas.js │ │ ├── relative_date.js │ │ ├── pickup_dates.js │ │ ├── html2canvas │ │ ├── Renderer.js │ │ ├── Font.js │ │ ├── Support.js │ │ ├── Util.js │ │ └── Queue.js │ │ ├── analytics.js.coffee │ │ ├── jquery.bootstrap-growl.js │ │ └── jquery.hotkeys.js │ └── stylesheets │ ├── .gitkeep │ └── shared │ └── patch.scss ├── public ├── favicon.ico ├── images │ ├── evernote16.png │ ├── wripe114s.png │ ├── wripe400s.png │ ├── evernote16b.png │ ├── masuidrive64.jpg │ ├── wripe-top880.png │ ├── dropbox_icon128.png │ └── evernote_icon128.png ├── fonts │ ├── fontawesome-webfont-311.ttf │ └── fontawesome-webfont-311.woff ├── top.html ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── deploy.sh ├── Procfile ├── bin ├── rake ├── bundle └── rails ├── db ├── migrate │ ├── 20130506012327_add_email_to_users.rb │ ├── 20130401093441_add_icon_url_to_users.rb │ ├── 20130529064128_add_user_agent_to_feedbacks.rb │ ├── 20130502030218_add_backups_to_dropbox_users.rb │ ├── 20130424082452_add_lock_version_to_page.rb │ ├── 20130619162458_remove_expiration_form_evernote_users.rb │ ├── 20130602120810_add_whatsnew_to_helps.rb │ ├── 20130619175110_add_notebook_guid_to_evernote_user.rb │ ├── 20131014131119_remove_too_long_tags.rb │ ├── 20130621122817_add_active_to_users.rb │ ├── 20130505061158_add_actived_at_to_users.rb │ ├── 20131014145849_update_taggings.rb │ ├── 20130425163314_add_archived_to_page.rb │ ├── 20130417115449_add_rw_permission_to_page.rb │ ├── 20130401042055_create_users.rb │ ├── 20130523155902_add_sender_user_id_to_user_messages.rb │ ├── 20130502015700_create_dropbox_users.rb │ ├── 20130401093723_alter_fb_user.rb │ ├── 20130415152846_create_chatrooms.rb │ ├── 20130502061752_add_page_taggings_count_to_page_tags.rb │ ├── 20130401043234_create_fb_users.rb │ ├── 20130415152926_create_chat_messages.rb │ ├── 20130502040654_create_page_taggings.rb │ ├── 20130415154159_create_chatroom_user.rb │ ├── 20130606071320_create_page_properties.rb │ ├── 20130429044630_create_page_dates.rb │ ├── 20130424073917_create_page_histories.rb │ ├── 20130523155845_rename_message_type.rb │ ├── 20130416080847_create_pages.rb │ ├── 20130426090032_create_gh_users.rb │ ├── 20130502040619_create_page_tags.rb │ ├── 20130523161758_create_helps.rb │ ├── 20130430155039_add_modified_at_to_page.rb │ ├── 20130606071309_create_user_properties.rb │ ├── 20130417122536_create_page_users.rb │ ├── 20130619142109_create_evernote_users.rb │ ├── 20130505004446_create_user_messages.rb │ └── 20130529031512_create_feedbacks.rb └── seeds.rb ├── spec ├── support │ ├── window.rb │ ├── test_login.rb │ ├── database_cleaner.rb │ ├── wait_until.rb │ └── capybara_drivers.rb ├── features │ ├── guest_spec.rb │ ├── signin_facebook_spec.rb │ ├── create_new_page_spec.rb │ ├── signin_github_spec.rb │ ├── search_spec.rb │ ├── signin_evernote_spec.rb │ ├── modify_page_spec.rb │ └── evernote_sync_spec.rb ├── factories │ └── users.rb └── spec_helper.rb ├── config ├── paperclip_feedbacks_s3-sample.yml ├── boot.rb ├── initializers │ ├── dependencies.rb │ ├── session_store.rb │ ├── filter_parameter_logging.rb │ ├── mime_types.rb │ ├── backtrace_silencers.rb │ ├── wrap_parameters.rb │ ├── secret_token.rb │ └── inflections.rb ├── application-sample.yml ├── environment.rb ├── dropbox-sample.yml ├── sunspot.yml ├── locales │ ├── en.bootstrap.yml │ └── en.yml ├── github-sample.yml ├── facebook-sample.yml ├── async-sample.yml ├── unicorn.rb ├── database.yml ├── evernote-sample.yml ├── application.rb ├── environments │ ├── development.rb │ ├── test.rb │ └── production.rb └── routes.rb ├── config.ru ├── doc └── README_FOR_APP ├── script └── rails ├── .gitignore ├── Rakefile ├── README.md └── Gemfile /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/functional/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/app_helper.rb: -------------------------------------------------------------------------------- 1 | module AppHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/helps_helper.rb: -------------------------------------------------------------------------------- 1 | module HelpsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/pages_helper.rb: -------------------------------------------------------------------------------- 1 | module PagesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/stats_helper.rb: -------------------------------------------------------------------------------- 1 | module StatsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/exports_helper.rb: -------------------------------------------------------------------------------- 1 | module ExportsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/fbauth_helper.rb: -------------------------------------------------------------------------------- 1 | module FbauthHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/ghauth_helper.rb: -------------------------------------------------------------------------------- 1 | module GhauthHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/calendar_helper.rb: -------------------------------------------------------------------------------- 1 | module CalendarHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/feedbacks_helper.rb: -------------------------------------------------------------------------------- 1 | module FeedbacksHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/messages_helper.rb: -------------------------------------------------------------------------------- 1 | module MessagesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/settings_helper.rb: -------------------------------------------------------------------------------- 1 | module SettingsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/preview.js.coffee: -------------------------------------------------------------------------------- 1 | #= require shared/marked 2 | -------------------------------------------------------------------------------- /app/helpers/dropbox_auth_helper.rb: -------------------------------------------------------------------------------- 1 | module DropboxAuthHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/evernote_auth_helper.rb: -------------------------------------------------------------------------------- 1 | module EvernoteAuthHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/user_messages_helper.rb: -------------------------------------------------------------------------------- 1 | module UserMessagesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/pages/members_helper.rb: -------------------------------------------------------------------------------- 1 | module Pages::MembersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/assets/stylesheets/settings.scss: -------------------------------------------------------------------------------- 1 | .container-fluid { 2 | padding-top: 32px; 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/sh 2 | 3 | rake assets:precompile 4 | jammit-s3 5 | git push heroku master 6 | -------------------------------------------------------------------------------- /app/models/dropbox_user.rb: -------------------------------------------------------------------------------- 1 | class DropboxUser < ActiveRecord::Base 2 | belongs_to :user 3 | end 4 | -------------------------------------------------------------------------------- /app/models/page_history.rb: -------------------------------------------------------------------------------- 1 | class PageHistory < ActiveRecord::Base 2 | belongs_to :page 3 | end 4 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb 2 | worker: bundle exec rake queue_runner 3 | -------------------------------------------------------------------------------- /public/images/evernote16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/images/evernote16.png -------------------------------------------------------------------------------- /public/images/wripe114s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/images/wripe114s.png -------------------------------------------------------------------------------- /public/images/wripe400s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/images/wripe400s.png -------------------------------------------------------------------------------- /app/models/page_date.rb: -------------------------------------------------------------------------------- 1 | class PageDate < ActiveRecord::Base 2 | belongs_to :page 3 | belongs_to :user 4 | end 5 | -------------------------------------------------------------------------------- /app/models/page_user.rb: -------------------------------------------------------------------------------- 1 | class PageUser < ActiveRecord::Base 2 | belongs_to :page 3 | belongs_to :user 4 | end 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /public/images/evernote16b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/images/evernote16b.png -------------------------------------------------------------------------------- /public/images/masuidrive64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/images/masuidrive64.jpg -------------------------------------------------------------------------------- /public/images/wripe-top880.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/images/wripe-top880.png -------------------------------------------------------------------------------- /test/helpers/app_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AppHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/top_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TopHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /public/images/dropbox_icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/images/dropbox_icon128.png -------------------------------------------------------------------------------- /test/helpers/stats_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class StatsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /public/images/evernote_icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/images/evernote_icon128.png -------------------------------------------------------------------------------- /test/helpers/sessions_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/settings_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SettingsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/unit/helpers/fbauth_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class FbauthHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/unit/helpers/ghauth_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class GhauthHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/unit/helpers/pages_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PagesHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /app/models/settings.rb: -------------------------------------------------------------------------------- 1 | class Settings < Settingslogic 2 | source "#{Rails.root}/config/application.yml" 3 | namespace Rails.env 4 | end -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont-311.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/fonts/fontawesome-webfont-311.ttf -------------------------------------------------------------------------------- /test/helpers/dropbox_auth_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DropboxAuthHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/user_messages_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserMessagesHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/unit/helpers/dashboard_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DashboardHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont-311.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masuidrive/open-wripe/HEAD/public/fonts/fontawesome-webfont-311.woff -------------------------------------------------------------------------------- /test/unit/helpers/pages/members_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Pages::MembersHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/app_controller.rb: -------------------------------------------------------------------------------- 1 | class AppController < ApplicationController 2 | def index 3 | render :layout => false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/models/page_tagging.rb: -------------------------------------------------------------------------------- 1 | class PageTagging < ActiveRecord::Base 2 | belongs_to :page 3 | belongs_to :page_tag, :counter_cache => true 4 | end 5 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /public/top.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | go to /app 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/splash.js.coffee: -------------------------------------------------------------------------------- 1 | 2 | class SplashDialog extends ModalDialog 3 | el: $('#splash-dialog') 4 | 5 | window.SplashDialog = SplashDialog -------------------------------------------------------------------------------- /app/controllers/user_messages_controller.rb: -------------------------------------------------------------------------------- 1 | class UserMessagesController < ApplicationController 2 | before_filter :required_login 3 | 4 | # TODO 5 | end 6 | -------------------------------------------------------------------------------- /app/models/page_property.rb: -------------------------------------------------------------------------------- 1 | class PageProperty < ActiveRecord::Base 2 | belongs_to :page 3 | validates :key, :uniqueness => { :scope => :page_id } 4 | end 5 | -------------------------------------------------------------------------------- /app/models/user_property.rb: -------------------------------------------------------------------------------- 1 | class UserProperty < ActiveRecord::Base 2 | belongs_to :user 3 | validates :key, :uniqueness => { :scope => :user_id } 4 | end 5 | -------------------------------------------------------------------------------- /test/unit/page_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PageTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /test/unit/fb_user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class FbUserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/gh_user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class GhUserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130506012327_add_email_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddEmailToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :email, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/models/page_tag_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PageTagTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/page_user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PageUserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130401093441_add_icon_url_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddIconUrlToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :icon_url, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/window.rb: -------------------------------------------------------------------------------- 1 | def within_window(name) 2 | sleep 0.5 3 | original = current_window 4 | use_window(name) 5 | yield 6 | ensure 7 | use_window(original) 8 | end 9 | -------------------------------------------------------------------------------- /test/models/page_date_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PageDateTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/unit/page_history_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PageHistoryTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/dropbox_user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DropboxUserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/page_tagging_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PageTaggingTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/user_message_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserMessageTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | username: MyString 5 | 6 | two: 7 | username: MyString 8 | -------------------------------------------------------------------------------- /config/paperclip_feedbacks_s3-sample.yml: -------------------------------------------------------------------------------- 1 | production: 2 | bucket: <%= ENV['S3_FEEDBACK_BUCKET'] %> 3 | access_key_id: <%= ENV['S3_KEY'] %> 4 | secret_access_key: <%= ENV['S3_SECRET'] %> 5 | -------------------------------------------------------------------------------- /test/controllers/app_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AppControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/top_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TopControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | use Rack::Deflater 5 | run Wripe::Application 6 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 5 | -------------------------------------------------------------------------------- /config/initializers/dependencies.rb: -------------------------------------------------------------------------------- 1 | require './lib/evernote_auth' 2 | require './lib/backup_dropbox' 3 | require './lib/prop_extend' 4 | require './lib/ar_async' 5 | require './lib/block_profiler' 6 | -------------------------------------------------------------------------------- /db/migrate/20130529064128_add_user_agent_to_feedbacks.rb: -------------------------------------------------------------------------------- 1 | class AddUserAgentToFeedbacks < ActiveRecord::Migration 2 | def change 3 | add_column :feedbacks, :user_agent, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/stats_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class StatsControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130502030218_add_backups_to_dropbox_users.rb: -------------------------------------------------------------------------------- 1 | class AddBackupsToDropboxUsers < ActiveRecord::Migration 2 | def change 3 | add_column :dropbox_users, :backups, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130424082452_add_lock_version_to_page.rb: -------------------------------------------------------------------------------- 1 | class AddLockVersionToPage < ActiveRecord::Migration 2 | def change 3 | add_column :pages, :lock_version, :integer, :default => 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/dropbox_auth_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DropboxAuthControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/user_messages_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserMessagesControllerTest < ActionController::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/exports.css.less: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the exports controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Less here: http://lesscss.org/ 4 | -------------------------------------------------------------------------------- /config/application-sample.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | flags: 3 | disable_ar_async: off 4 | 5 | development: 6 | <<: *defaults 7 | 8 | test: 9 | <<: *defaults 10 | 11 | production: 12 | <<: *defaults -------------------------------------------------------------------------------- /db/migrate/20130619162458_remove_expiration_form_evernote_users.rb: -------------------------------------------------------------------------------- 1 | class RemoveExpirationFormEvernoteUsers < ActiveRecord::Migration 2 | def change 3 | remove_column :evernote_users, :expiration 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/feedbacks.css.less: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the feedbacks controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Less here: http://lesscss.org/ 4 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # 2 | WRIPE_VERSION = 11 3 | 4 | # Load the rails application. 5 | require File.expand_path('../application', __FILE__) 6 | 7 | # Initialize the rails application. 8 | Wripe::Application.initialize! 9 | -------------------------------------------------------------------------------- /db/migrate/20130602120810_add_whatsnew_to_helps.rb: -------------------------------------------------------------------------------- 1 | class AddWhatsnewToHelps < ActiveRecord::Migration 2 | def up 3 | User.all.each do |user| 4 | user.helps.create key: 'whatsnew' 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130619175110_add_notebook_guid_to_evernote_user.rb: -------------------------------------------------------------------------------- 1 | class AddNotebookGuidToEvernoteUser < ActiveRecord::Migration 2 | def change 3 | add_column :evernote_users, :notebook_guid, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131014131119_remove_too_long_tags.rb: -------------------------------------------------------------------------------- 1 | class RemoveTooLongTags < ActiveRecord::Migration 2 | def up 3 | PageTag.where('LENGTH(page_tags.name) > 32').destroy_all 4 | end 5 | def down 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/evernote_auth.css.less: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the evernote_auth controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Less here: http://lesscss.org/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/messages.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the messages controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Wripe::Application.config.session_store :cookie_store, { 4 | key: '_wripe_session', 5 | expire_after: 2.years 6 | } 7 | -------------------------------------------------------------------------------- /db/migrate/20130621122817_add_active_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddActiveToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :active, :boolean, default: true 4 | add_index :users, :active 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20130505061158_add_actived_at_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddActivedAtToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :actived_at, :timestamp 4 | add_index :users, [:actived_at] 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /test/fixtures/page_taggings.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | page_id: 1 5 | page_tag_id: 1 6 | 7 | two: 8 | page_id: 1 9 | page_tag_id: 1 10 | -------------------------------------------------------------------------------- /app/views/sessions/test.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | Go to top 4 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /db/migrate/20131014145849_update_taggings.rb: -------------------------------------------------------------------------------- 1 | class UpdateTaggings < ActiveRecord::Migration 2 | def up 3 | Page.find_each do |page| 4 | page.send 'generate_tags' 5 | end 6 | end 7 | def down 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/shared/_help.html.erb: -------------------------------------------------------------------------------- 1 |
2 | × 3 |
<%= raw title %>
4 |
5 | <% body_block.call %> 6 |
7 |
8 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /test/controllers/settings_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SettingsControllerTest < ActionController::TestCase 4 | test "should get index" do 5 | get :index 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20130425163314_add_archived_to_page.rb: -------------------------------------------------------------------------------- 1 | class AddArchivedToPage < ActiveRecord::Migration 2 | def change 3 | add_column :pages, :archived, :boolean, :default => false 4 | add_index :pages, [:user_id, :archived, :updated_at] 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/fixtures/page_dates.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | page_id: 1 5 | user_id: 1 6 | date: 2013-04-29 7 | 8 | two: 9 | page_id: 1 10 | user_id: 1 11 | date: 2013-04-29 12 | -------------------------------------------------------------------------------- /test/fixtures/page_histories.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | page_id: 1 5 | body: MyText 6 | title: MyText 7 | 8 | two: 9 | page_id: 1 10 | body: MyText 11 | title: MyText 12 | -------------------------------------------------------------------------------- /test/fixtures/page_tags.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | user_id: 1 5 | name: MyString 6 | pages_count: 1 7 | 8 | two: 9 | user_id: 1 10 | name: MyString 11 | pages_count: 1 12 | -------------------------------------------------------------------------------- /app/assets/javascripts/messages.js.coffee: -------------------------------------------------------------------------------- 1 | msg = 2 | english_months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] 3 | wdays: ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat' ,'Sun'] 4 | 5 | window.msg = msg -------------------------------------------------------------------------------- /spec/features/guest_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | feature 'Guest', :js => true do 4 | scenario 'access to app page' do 5 | visit '/app' 6 | sleep 2.0 # waiting redirection in Javascript 7 | current_path.should.should == '/' 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/exports_controller.rb: -------------------------------------------------------------------------------- 1 | require './lib/backup_pages' 2 | 3 | class ExportsController < ApplicationController 4 | before_filter :required_login 5 | 6 | def zip 7 | zipfile = BackupPages.zip(current_user) 8 | send_file zipfile 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20130417115449_add_rw_permission_to_page.rb: -------------------------------------------------------------------------------- 1 | class AddRwPermissionToPage < ActiveRecord::Migration 2 | def change 3 | add_column :pages, :read_permission, :integer, :default => 0 4 | add_column :pages, :write_permission, :integer, :default => 0 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | User-Agent: * 7 | Disallow: /pages 8 | 9 | -------------------------------------------------------------------------------- /db/migrate/20130401042055_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table :users do |t| 4 | t.string :username 5 | 6 | t.timestamps 7 | end 8 | add_index :users, :username, :unique => true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/models/help.rb: -------------------------------------------------------------------------------- 1 | class Help < ActiveRecord::Base 2 | belongs_to :user 3 | scope :key, -> k { where(:key => k) } 4 | validates :key, :uniqueness => { :scope => :user_id } 5 | 6 | def to_hash(*args) 7 | { 8 | key: key, 9 | value: value 10 | } 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/fixtures/dropbox_users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | user_id: 1 5 | auth_token: MyString 6 | auth_secret: MyString 7 | 8 | two: 9 | user_id: 1 10 | auth_token: MyString 11 | auth_secret: MyString 12 | -------------------------------------------------------------------------------- /test/fixtures/pages.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | user_id: 1 5 | title: MyText 6 | body: MyText 7 | key: MyString 8 | 9 | two: 10 | user_id: 1 11 | title: MyText 12 | body: MyText 13 | key: MyString 14 | -------------------------------------------------------------------------------- /test/fixtures/fb_users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | user_id: 1 5 | token: MyText 6 | name: MyText 7 | account: MyText 8 | 9 | two: 10 | user_id: 2 11 | token: MyText 12 | name: MyText 13 | account: MyText 14 | -------------------------------------------------------------------------------- /test/fixtures/gh_users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | user_id: 1 5 | token: MyText 6 | name: MyText 7 | account: MyText 8 | 9 | two: 10 | user_id: 1 11 | token: MyText 12 | name: MyText 13 | account: MyText 14 | -------------------------------------------------------------------------------- /lib/fbauth.rb: -------------------------------------------------------------------------------- 1 | module FbAuth 2 | def self.config 3 | @config ||= YAML.load(open(File.join(Rails.root, 'config', 'facebook.yml')))[Rails.env] 4 | end 5 | 6 | def self.oauth 7 | @oauth ||= Koala::Facebook::OAuth.new(config['application_id'], config['application_secret'], config['callback']) 8 | end 9 | end -------------------------------------------------------------------------------- /db/migrate/20130523155902_add_sender_user_id_to_user_messages.rb: -------------------------------------------------------------------------------- 1 | class AddSenderUserIdToUserMessages < ActiveRecord::Migration 2 | def change 3 | add_column :user_messages, :sender_user_id, :integer 4 | add_index :user_messages, [:user_id, :sender_user_id, :created_at], :name => 'idx_user_messages_6' 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= @page_title || 'wri.pe' %> 6 | <%= stylesheet_link_tag controller.controller_name, :media => "all" %> 7 | <%= csrf_meta_tag %> 8 | 9 | 10 | <%= yield %> 11 | 12 | -------------------------------------------------------------------------------- /config/dropbox-sample.yml: -------------------------------------------------------------------------------- 1 | development: 2 | app_key: XXXXXXX 3 | app_secret: XXXXXXX 4 | mode: sandbox 5 | 6 | test: 7 | app_key: XXXXXXX 8 | app_secret: XXXXXXX 9 | mode: sandbox 10 | 11 | production: 12 | app_key: <%= ENV['DROPBOX_KEY'] %> 13 | app_secret: <%= ENV['DROPBOX_SECRET'] %> 14 | mode: sandbox 15 | -------------------------------------------------------------------------------- /db/migrate/20130502015700_create_dropbox_users.rb: -------------------------------------------------------------------------------- 1 | class CreateDropboxUsers < ActiveRecord::Migration 2 | def change 3 | create_table :dropbox_users do |t| 4 | t.integer :user_id, :unique => true 5 | t.string :auth_token 6 | t.string :auth_secret 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/prop_extend.rb: -------------------------------------------------------------------------------- 1 | module PropExtend 2 | def [](k) 3 | prop = where(:key => k).first 4 | prop ? prop.value : nil 5 | end 6 | 7 | def []=(k, v) 8 | prop = where(:key => k).first 9 | if prop 10 | prop.update_attribute :value, v 11 | else 12 | create(key: k, value: v) 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /test/fixtures/user_messages.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | user_id: 1 5 | page_id: 1 6 | read: false 7 | title: MyText 8 | body: MyText 9 | 10 | two: 11 | user_id: 1 12 | page_id: 1 13 | read: false 14 | title: MyText 15 | body: MyText 16 | -------------------------------------------------------------------------------- /db/migrate/20130401093723_alter_fb_user.rb: -------------------------------------------------------------------------------- 1 | class AlterFbUser < ActiveRecord::Migration 2 | def up 3 | rename_column :fb_users, :account, :fbid 4 | rename_column :fb_users, :name, :json 5 | end 6 | 7 | def down 8 | rename_column :fb_users, :fbid, :account 9 | rename_column :fb_users, :json, :name 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130415152846_create_chatrooms.rb: -------------------------------------------------------------------------------- 1 | class CreateChatrooms < ActiveRecord::Migration 2 | def change 3 | create_table :chatrooms do |t| 4 | t.string :key, :unique => true 5 | t.string :name 6 | t.string :user_id 7 | 8 | t.timestamps 9 | end 10 | add_index :chatrooms, :user_id 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20130502061752_add_page_taggings_count_to_page_tags.rb: -------------------------------------------------------------------------------- 1 | class AddPageTaggingsCountToPageTags < ActiveRecord::Migration 2 | def change 3 | add_column :page_tags, :page_taggings_count, :integer, :default => 0 4 | remove_column :page_tags, :pages_count 5 | add_index :page_tags, [:user_id, :page_taggings_count] 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130401043234_create_fb_users.rb: -------------------------------------------------------------------------------- 1 | class CreateFbUsers < ActiveRecord::Migration 2 | def change 3 | create_table :fb_users do |t| 4 | t.integer :user_id 5 | t.text :token 6 | t.text :name 7 | t.text :account 8 | 9 | t.timestamps 10 | end 11 | add_index :fb_users, :user_id 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/support/test_login.rb: -------------------------------------------------------------------------------- 1 | def clear_session 2 | visit "/sessions/test" 3 | end 4 | 5 | def test_login(username, do_clear_session=true) 6 | clear_session if do_clear_session 7 | visit "/sessions/test?username=#{username}" 8 | wait_until_visible "#nav-username-link" 9 | evaluate_script('session.username()').should == username 10 | end 11 | -------------------------------------------------------------------------------- /app/assets/javascripts/shared/defer.js.coffee: -------------------------------------------------------------------------------- 1 | window.Deferred = (callback) -> 2 | defer = $.Deferred() 3 | if callback 4 | #try 5 | callback(defer) 6 | #catch error 7 | # if console && console.log 8 | # console.log(error, error.message) 9 | # defer.reject('fatal', error) 10 | # defer.promise() 11 | defer 12 | -------------------------------------------------------------------------------- /db/migrate/20130415152926_create_chat_messages.rb: -------------------------------------------------------------------------------- 1 | class CreateChatMessages < ActiveRecord::Migration 2 | def change 3 | create_table :chat_messages do |t| 4 | t.integer :user_id 5 | t.integer :chatroom_id 6 | t.text :body 7 | 8 | t.timestamps 9 | end 10 | add_index :chat_messages, :chatroom_id 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/views/sessions/destroy.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | Go to top 14 | 15 | 16 | -------------------------------------------------------------------------------- /db/migrate/20130502040654_create_page_taggings.rb: -------------------------------------------------------------------------------- 1 | class CreatePageTaggings < ActiveRecord::Migration 2 | def change 3 | create_table :page_taggings do |t| 4 | t.integer :page_id 5 | t.integer :page_tag_id 6 | 7 | t.timestamps 8 | end 9 | add_index :page_taggings, [:page_id, :page_tag_id], :unique => true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130415154159_create_chatroom_user.rb: -------------------------------------------------------------------------------- 1 | class CreateChatroomUser < ActiveRecord::Migration 2 | def change 3 | create_table :chatroom_users do |t| 4 | t.integer :chatroom_id 5 | t.integer :user_id 6 | 7 | t.timestamps 8 | end 9 | add_index :chatroom_users, [:chatroom_id, :user_id], :unique => true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /db/migrate/20130606071320_create_page_properties.rb: -------------------------------------------------------------------------------- 1 | class CreatePageProperties < ActiveRecord::Migration 2 | def change 3 | create_table :page_properties do |t| 4 | t.integer :page_id 5 | t.string :key 6 | t.text :value 7 | 8 | t.timestamps 9 | end 10 | add_index :page_properties, [:page_id, :key], :unique => true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/models/page_tag.rb: -------------------------------------------------------------------------------- 1 | class PageTag < ActiveRecord::Base 2 | belongs_to :user 3 | has_many :page_taggings, :dependent => :destroy 4 | has_many :pages, :through => :page_taggings 5 | scope :tag_name, -> tag_name { where(:name => tag_name) } 6 | 7 | def to_hash 8 | { 9 | name: name, 10 | pages_count: page_taggings_count 11 | } 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /config/sunspot.yml: -------------------------------------------------------------------------------- 1 | production: 2 | solr: 3 | hostname: localhost 4 | port: 8981 5 | log_level: WARNING 6 | # read_timeout: 2 7 | # open_timeout: 0.5 8 | 9 | development: 10 | solr: 11 | hostname: localhost 12 | port: 8982 13 | log_level: INFO 14 | 15 | test: 16 | solr: 17 | hostname: localhost 18 | port: 8983 19 | log_level: WARNING 20 | -------------------------------------------------------------------------------- /db/migrate/20130429044630_create_page_dates.rb: -------------------------------------------------------------------------------- 1 | class CreatePageDates < ActiveRecord::Migration 2 | def change 3 | create_table :page_dates do |t| 4 | t.integer :page_id 5 | t.integer :user_id 6 | t.date :date 7 | 8 | t.timestamps 9 | end 10 | add_index :page_dates, [:page_id] 11 | add_index :page_dates, [:user_id, :date] 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130424073917_create_page_histories.rb: -------------------------------------------------------------------------------- 1 | class CreatePageHistories < ActiveRecord::Migration 2 | def change 3 | create_table :page_histories do |t| 4 | t.integer :page_id 5 | t.text :body 6 | t.text :title 7 | t.integer :page_lock_version 8 | 9 | t.timestamps 10 | end 11 | add_index :page_histories, [:page_id, :created_at] 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130523155845_rename_message_type.rb: -------------------------------------------------------------------------------- 1 | class RenameMessageType < ActiveRecord::Migration 2 | def change 3 | rename_column :user_messages, :type, :message_type 4 | add_index :user_messages, [:user_id, :message_type, :created_at], :name => 'idx_user_messages_4' 5 | add_index :user_messages, [:user_id, :message_type, :read, :created_at], :name => 'idx_user_messages_5' 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/fixtures/page_users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | page_id: 1 5 | user_id: 1 6 | read_permission: false 7 | write_permission: false 8 | share_permission: false 9 | 10 | two: 11 | page_id: 1 12 | user_id: 2 13 | read_permission: false 14 | write_permission: false 15 | share_permission: false 16 | -------------------------------------------------------------------------------- /db/migrate/20130416080847_create_pages.rb: -------------------------------------------------------------------------------- 1 | class CreatePages < ActiveRecord::Migration 2 | def change 3 | create_table :pages do |t| 4 | t.integer :user_id 5 | t.text :title 6 | t.text :body 7 | t.string :key, :unique => true 8 | t.integer :share_status 9 | 10 | t.timestamps 11 | end 12 | add_index :pages, [:user_id, :updated_at] 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20130426090032_create_gh_users.rb: -------------------------------------------------------------------------------- 1 | class CreateGhUsers < ActiveRecord::Migration 2 | def change 3 | create_table :gh_users do |t| 4 | t.integer :user_id 5 | t.string :ghid, :unique => true 6 | t.text :token 7 | t.text :name 8 | t.text :account 9 | t.text :json 10 | 11 | t.timestamps 12 | end 13 | add_index :gh_users, :user_id 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :testdrive1, :class => User do 3 | username 'testdrive1' 4 | email 'testdrive1@example.com' 5 | icon_url 'http://example.com/testdrive1.png' 6 | end 7 | 8 | factory :testdrive2, :class => User do 9 | username 'testdrive2' 10 | email 'testdrive2@example.com' 11 | icon_url 'http://example.com/testdrive2.png' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130502040619_create_page_tags.rb: -------------------------------------------------------------------------------- 1 | class CreatePageTags < ActiveRecord::Migration 2 | def change 3 | create_table :page_tags do |t| 4 | t.integer :user_id 5 | t.string :name 6 | t.integer :pages_count, :default => 0 7 | 8 | t.timestamps 9 | end 10 | add_index :page_tags, [:user_id, :name], :unique => true 11 | add_index :page_tags, [:user_id, :pages_count] 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20130523161758_create_helps.rb: -------------------------------------------------------------------------------- 1 | class CreateHelps < ActiveRecord::Migration 2 | def change 3 | create_table :helps do |t| 4 | t.integer :user_id 5 | t.string :key 6 | t.string :value 7 | 8 | t.timestamps 9 | end 10 | add_index :helps, [:user_id] 11 | add_index :helps, [:user_id, :key], :unique => true 12 | User.all.each(&:create_default_helps) rescue nil 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20130430155039_add_modified_at_to_page.rb: -------------------------------------------------------------------------------- 1 | class AddModifiedAtToPage < ActiveRecord::Migration 2 | def change 3 | add_column :pages, :modified_at, :integer 4 | add_index :pages, [:user_id, :modified_at] 5 | add_index :pages, [:user_id, :archived, :modified_at] 6 | Page.all.each do |page| 7 | page.update_attribute :modified_at, page.updated_at.to_i 8 | end 9 | Page.reindex 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/performance/browsing_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'rails/performance_test_help' 3 | 4 | class BrowsingTest < ActionDispatch::PerformanceTest 5 | # Refer to the documentation for all available options 6 | # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory] 7 | # :output => 'tmp/performance', :formats => [:flat] } 8 | 9 | def test_homepage 10 | get '/' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/html2canvas.js: -------------------------------------------------------------------------------- 1 | //= require shared/html2canvas/Core 2 | //= require shared/html2canvas/Font 3 | //= require shared/html2canvas/Generate 4 | //= require shared/html2canvas/Queue 5 | //= require shared/html2canvas/Parse 6 | //= require shared/html2canvas/Preload 7 | //= require shared/html2canvas/Renderer 8 | //= require shared/html2canvas/Support 9 | //= require shared/html2canvas/Util 10 | //= require shared/html2canvas/renderers/Canvas -------------------------------------------------------------------------------- /db/migrate/20130606071309_create_user_properties.rb: -------------------------------------------------------------------------------- 1 | class CreateUserProperties < ActiveRecord::Migration 2 | def change 3 | create_table :user_properties do |t| 4 | t.integer :user_id 5 | t.string :key 6 | t.text :value 7 | 8 | t.timestamps 9 | end 10 | add_index :user_properties, [:user_id, :key], :unique => true 11 | 12 | User.all.each do |user| 13 | user.create_default_properties 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/ghauth.rb: -------------------------------------------------------------------------------- 1 | module GhAuth 2 | def self.config 3 | @config ||= YAML.load(open(File.join(Rails.root, 'config', 'github.yml')))[Rails.env] 4 | end 5 | 6 | def self.oauth 7 | @oauth ||= OAuth2::Client.new(config['client_id'], config['client_secret'], { 8 | site: 'https://api.github.com', 9 | authorize_url: 'https://github.com/login/oauth/authorize', 10 | token_url: 'https://github.com/login/oauth/access_token' 11 | }) 12 | end 13 | end -------------------------------------------------------------------------------- /app/controllers/helps_controller.rb: -------------------------------------------------------------------------------- 1 | class HelpsController < ApplicationController 2 | before_filter :required_login 3 | 4 | def reset 5 | current_user.create_default_helps 6 | 7 | respond_to do |format| 8 | format.json do 9 | render json: current_user.helps.map(&:to_hash) 10 | end 11 | end 12 | end 13 | 14 | def destroy 15 | help = current_user.helps.key(params[:id]).first 16 | help.destroy if help 17 | head 200 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/assets/javascripts/shared/labeled_button.js.coffee: -------------------------------------------------------------------------------- 1 | class LabeledButton 2 | constructor: (@el) -> 3 | label: (name) -> 4 | if name 5 | $('span', @el).hide() 6 | @name = name 7 | $("span[name=#{name}]", @el).show() 8 | else 9 | @name 10 | 11 | click: (callback) -> 12 | @el.click callback 13 | 14 | enable: -> 15 | @el.removeClass('disabled') 16 | 17 | disable: -> 18 | @el.addClass('disabled') 19 | 20 | 21 | window.LabeledButton = LabeledButton 22 | -------------------------------------------------------------------------------- /app/views/dropbox_auth/callback.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dropbox auth - wri.pe 7 | 15 | 16 | Close this window 17 | 18 | -------------------------------------------------------------------------------- /app/views/shared/_top_nav.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /lib/backup_pages.rb: -------------------------------------------------------------------------------- 1 | # 2 | require 'zip/zip' 3 | 4 | module BackupPages 5 | def self.zip(user) 6 | zipfile = '%s/wripe.backup.dropbox.%d.zip' % [Dir.tmpdir, user.id] 7 | FileUtils.rm_rf zipfile if File.exists?(zipfile) 8 | Zip::ZipFile.open(zipfile, Zip::ZipFile::CREATE) do |zip| 9 | user.pages.each do |page| 10 | zip.get_output_stream("page-#{page.id}.txt") do |f| 11 | f.write [page.title, page.body].join("\n") 12 | end 13 | end 14 | end 15 | zipfile 16 | end 17 | end -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] = "test" 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. 7 | # 8 | # Note: You'll currently still have to declare fixtures explicitly in integration tests 9 | # -- they do not yet inherit this setting 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | end 14 | -------------------------------------------------------------------------------- /config/locales/en.bootstrap.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | helpers: 6 | actions: "Actions" 7 | links: 8 | back: "Back" 9 | cancel: "Cancel" 10 | confirm: "Are you sure?" 11 | destroy: "Delete" 12 | new: "New" 13 | titles: 14 | edit: "Edit" 15 | save: "Save" 16 | new: "New" 17 | delete: "Delete" 18 | -------------------------------------------------------------------------------- /app/assets/javascripts/shared/app_cache.js.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | $ -> 3 | appCache = window.applicationCache 4 | if appCache 5 | upgrade = -> 6 | if confirm('Could you upgrade to new version now?') 7 | if appCache.status == app.UPDATEREADY 8 | appCache.swapCache() 9 | location.reload() 10 | 11 | $(appCache).bind "updateready", -> 12 | upgrade() 13 | 14 | if appCache.status == app.UPDATEREADY 15 | upgrade() 16 | 17 | $(window).bind "online", -> 18 | appCache.update() 19 | ### -------------------------------------------------------------------------------- /config/github-sample.yml: -------------------------------------------------------------------------------- 1 | development: 2 | client_id: XXXXXXX 3 | client_secret: XXXXXXX 4 | callback: http://wripe.dev/ghauth/callback 5 | 6 | test: 7 | client_id: XXXXXXX 8 | client_secret: XXXXXXX 9 | callback: http://localhost:57124/ghauth/callback 10 | tests: 11 | - email: wripe@example.com 12 | password: wripe 13 | account: wripe 14 | 15 | production: 16 | client_id: <%= ENV['GITHUB_CLIENT_ID'] %> 17 | client_secret: <%= ENV['GITHUB_SECRET'] %> 18 | callback: <%= ENV['APP_BASE'] %>/ghauth/callback 19 | -------------------------------------------------------------------------------- /db/migrate/20130417122536_create_page_users.rb: -------------------------------------------------------------------------------- 1 | class CreatePageUsers < ActiveRecord::Migration 2 | def change 3 | create_table :page_users do |t| 4 | t.integer :page_id 5 | t.integer :user_id 6 | t.boolean :read_permission, :default => true 7 | t.boolean :write_permission, :default => true 8 | t.boolean :share_permission, :default => true 9 | 10 | t.timestamps 11 | end 12 | add_index :page_users, [:page_id, :user_id], :unique => true 13 | add_index :page_users, [:user_id] 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/paperclip_ext.rb: -------------------------------------------------------------------------------- 1 | # http://bendangelo.me/?p=60 2 | require 'paperclip' 3 | 4 | module Paperclip 5 | 6 | #converts a string into a file for paperclip to save 7 | # useage 8 | # self.avatar = Paperclip::string_to_file('bob.png', 'image/png', 'BASE64 here') 9 | def self.string_to_file(name, type, data) 10 | image = StringIO.new(data) 11 | image.class.class_eval { attr_accessor :original_filename, :content_type } 12 | image.original_filename = name 13 | image.content_type = type 14 | return image 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20130619142109_create_evernote_users.rb: -------------------------------------------------------------------------------- 1 | class CreateEvernoteUsers < ActiveRecord::Migration 2 | def change 3 | create_table :evernote_users do |t| 4 | t.integer :user_id 5 | t.integer :enid 6 | t.string :evernote_username 7 | t.string :access_token 8 | t.timestamp :expiration 9 | 10 | t.timestamps 11 | end 12 | add_index :evernote_users, [:enid], unique: true 13 | add_index :evernote_users, [:expiration], unique: true 14 | add_index :evernote_users, [:user_id], unique: true 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/assets/javascripts/shared/localstorage.js: -------------------------------------------------------------------------------- 1 | if(typeof window.localStorage == 'undefined') { 2 | window.localStorage = { 3 | clear: function() {}, 4 | getItem: function(key) {}, 5 | setItem: function(key, val) {}, 6 | removeItem: function(key) {}, 7 | key: function(key) {} 8 | }; 9 | } 10 | 11 | if(typeof window.sessionStorage == 'undefined') { 12 | window.sessionStorage = { 13 | clear: function() {}, 14 | getItem: function(key) {}, 15 | setItem: function(key, val) {}, 16 | removeItem: function(key) {}, 17 | key: function(key) {} 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /config/facebook-sample.yml: -------------------------------------------------------------------------------- 1 | development: 2 | application_id: 123456789 3 | application_secret: XXXXXXX 4 | callback: http://wripe.dev/fbauth/callback 5 | 6 | test: 7 | application_id: 123456789 8 | application_secret: XXXXXXX 9 | callback: http://localhost:57124/fbauth/callback 10 | tests: 11 | - email: test1@tfbnw.net 12 | password: wripe 13 | account: dorothy 14 | name: Test 15 | 16 | production: 17 | application_id: <%= ENV['FACEBOOK_APP_ID'] %> 18 | application_secret: <%= ENV['EVERNOTE_SECRET'] %> 19 | callback: <%= ENV['APP_BASE'] %>/fbauth/callback 20 | -------------------------------------------------------------------------------- /spec/support/database_cleaner.rb: -------------------------------------------------------------------------------- 1 | #vhttp://stackoverflow.com/questions/12326096/capybara-selenium-fault-and-redirect-example-com-when-without-everything-is-gre 2 | 3 | RSpec.configure do |config| 4 | config.use_transactional_fixtures = false 5 | 6 | config.before :each do 7 | if Capybara.current_driver == :rack_test 8 | DatabaseRewinder.strategy = :transaction 9 | else 10 | DatabaseRewinder.strategy = :truncation 11 | end 12 | DatabaseRewinder.start 13 | end 14 | 15 | config.after do 16 | DatabaseRewinder.clean 17 | Sunspot.remove_all! 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/async-sample.yml: -------------------------------------------------------------------------------- 1 | development: 2 | inline: true 3 | sqs: https://sqs.us-east-1.amazonaws.com/99999999999/wripe-ar-development 4 | access_key_id: XXXXXXX 5 | secret_access_key: XXXXXXX 6 | region: us-east-1 7 | 8 | test: 9 | inline: true 10 | sqs: https://sqs.us-east-1.amazonaws.com/99999999999/wripe-ar-test 11 | access_key_id: XXXXXXX 12 | secret_access_key: XXXXXXX 13 | region: us-east-1 14 | 15 | production: 16 | inline: false 17 | sqs: <%= ENV['S3_SQS'] %> 18 | access_key_id: <%= ENV['S3_KEY'] %> 19 | secret_access_key: <%= ENV['S3_SECRET'] %> 20 | region: <%= ENV['S3_REGION'] %> 21 | 22 | -------------------------------------------------------------------------------- /app/models/feedback.rb: -------------------------------------------------------------------------------- 1 | require './lib/paperclip_ext' 2 | require 'base64' 3 | 4 | class Feedback < ActiveRecord::Base 5 | belongs_to :user 6 | if %w(production).include?(Rails.env) 7 | has_attached_file :image, :storage => :s3, :s3_credentials => "#{Rails.root}/config/paperclip_feedbacks_s3.yml" 8 | else 9 | has_attached_file :image 10 | end 11 | 12 | SUBJECT_LABELS = ['Other', 'Bug', "Idea"] 13 | 14 | def image_data=(b64data) 15 | # remove image/png:base, 16 | data = Base64.decode64(b64data.split(',').pop) 17 | self.image = Paperclip::string_to_file('image.png', 'image/png', data) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure your secret_key_base is kept private 11 | # if you're sharing your code publicly. 12 | Wripe::Application.config.secret_key_base = ENV['SESSION_SECRET_KEY'] || 'SESSION_SECRET_KEY' 13 | -------------------------------------------------------------------------------- /app/controllers/feedbacks_controller.rb: -------------------------------------------------------------------------------- 1 | class FeedbacksController < ApplicationController 2 | before_filter :required_admin, except: [:create] 3 | 4 | def create 5 | @feedback = Feedback.new params.require(:feedback).permit(:body, :image_data, :subject) 6 | @feedback.user_agent = request.user_agent 7 | @feedback.user = current_user if current_user 8 | @feedback.save! 9 | 10 | respond_to do |format| 11 | format.html { head 201 } 12 | format.json { render json: [], status: 201 } 13 | end 14 | end 15 | 16 | def index 17 | @feedbacks = Feedback.order('feedbacks.id DESC').page(params[:page]) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/controllers/fbauth_controller.rb: -------------------------------------------------------------------------------- 1 | require "#{Rails.root}/lib/fbauth" 2 | 3 | class FbauthController < ApplicationController 4 | rescue_from OAuth::Unauthorized, :with => Proc.new { redirect_to '/' } 5 | 6 | def sign_in 7 | redirect_to FbAuth.oauth.url_for_oauth_code(:permissions => "email") 8 | end 9 | 10 | def callback 11 | user = FbUser.auth(FbAuth.oauth.get_access_token(params[:code])) 12 | session[:authorized_user_id] = user.id 13 | flash[:notice] = nil 14 | redirect_to :home 15 | rescue => e 16 | logger.error e.inspect 17 | flash[:notice] = I18n.t(:failed_facebook_login) 18 | redirect_to '/' 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20130505004446_create_user_messages.rb: -------------------------------------------------------------------------------- 1 | class CreateUserMessages < ActiveRecord::Migration 2 | def change 3 | create_table :user_messages do |t| 4 | t.integer :user_id 5 | t.integer :page_id 6 | t.boolean :read, :default => false 7 | t.string :type, :default => 'free' 8 | t.text :title 9 | t.text :body 10 | t.text :icon_url 11 | 12 | t.timestamps 13 | end 14 | add_index :user_messages, [:user_id, :created_at] 15 | add_index :user_messages, [:user_id, :read, :created_at] 16 | add_index :user_messages, [:user_id, :page_id, :read, :created_at], :name => 'idx_user_messages_3' 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def current_user 3 | controller.current_user 4 | end 5 | 6 | def render_top_nav(&block) 7 | body = block ? capture(&block) : '' 8 | render 'shared/top_nav', :body => body 9 | end 10 | 11 | def render_dialog(id, title, cancel:'Close', cancel_class:'', action:nil, &block) 12 | render :partial => 'shared/dialog', locals: { id: id, title: title, cancel_class: cancel_class, cancel_label: cancel, action: action, body_block: block } 13 | end 14 | 15 | def render_help(id, title, &block) 16 | render :partial => 'shared/help', locals: { id: id, title: title, body_block: block } 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /config/unicorn.rb: -------------------------------------------------------------------------------- 1 | worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3) 2 | timeout 15 3 | preload_app true 4 | 5 | before_fork do |server, worker| 6 | Signal.trap 'TERM' do 7 | puts 'Unicorn master intercepting TERM and sending myself QUIT instead' 8 | Process.kill 'QUIT', Process.pid 9 | end 10 | 11 | defined?(ActiveRecord::Base) and 12 | ActiveRecord::Base.connection.disconnect! 13 | end 14 | 15 | after_fork do |server, worker| 16 | Signal.trap 'TERM' do 17 | puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT' 18 | end 19 | 20 | defined?(ActiveRecord::Base) and 21 | ActiveRecord::Base.establish_connection 22 | end 23 | -------------------------------------------------------------------------------- /db/migrate/20130529031512_create_feedbacks.rb: -------------------------------------------------------------------------------- 1 | class CreateFeedbacks < ActiveRecord::Migration 2 | def change 3 | create_table :feedbacks do |t| 4 | t.integer :user_id 5 | t.boolean :closed 6 | t.boolean :opened 7 | t.integer :subject 8 | t.text :body 9 | t.text :admin_note 10 | 11 | t.timestamps 12 | end 13 | add_attachment :feedbacks, :image 14 | add_index :feedbacks, [:user_id, :created_at] 15 | add_index :feedbacks, [:opened, :created_at] 16 | add_index :feedbacks, [:closed, :created_at] 17 | add_index :feedbacks, [:subject, :created_at] 18 | add_index :feedbacks, [:closed, :subject, :created_at] 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/development.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: 16 | adapter: sqlite3 17 | database: db/test.sqlite3 18 | pool: 10 19 | timeout: 10000 20 | 21 | production: 22 | adapter: sqlite3 23 | database: db/production.sqlite3 24 | pool: 5 25 | timeout: 5000 26 | -------------------------------------------------------------------------------- /app/controllers/dropbox_auth_controller.rb: -------------------------------------------------------------------------------- 1 | require "#{Rails.root}/lib/backup_dropbox" 2 | 3 | class DropboxAuthController < ApplicationController 4 | before_filter :required_login 5 | 6 | def sign_in 7 | url, session[:dropbox] = DropboxBackup.new.request_auth(Rails.application.routes.url_helpers.dropbox_auth_callback_url) 8 | redirect_to url 9 | end 10 | 11 | def callback 12 | result = DropboxBackup.new.authorize(params[:oauth_token], session[:dropbox]) 13 | dbuser = (current_user.dropbox_user || current_user.create_dropbox_user) 14 | dbuser.update_attributes auth_token: result.token, auth_secret: result.secret 15 | current_user.helps.clear 'backup' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/views/shared/_dialog.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /lib/evernote_auth.rb: -------------------------------------------------------------------------------- 1 | require 'evernote_oauth' 2 | 3 | module EvernoteAuth 4 | def self.config 5 | @config ||= YAML.load(open(File.join(Rails.root, 'config', 'evernote.yml')))[Rails.env] 6 | end 7 | 8 | def self.oauth(token=nil) 9 | EvernoteOAuth::Client.new( 10 | token: token, 11 | consumer_key: config['consumer_key'], 12 | consumer_secret: config['consumer_secret'], 13 | sandbox: config['sandbox'] 14 | ) 15 | end 16 | 17 | def self.request_token 18 | EvernoteAuth.oauth.request_token(oauth_callback: config['callback']) 19 | end 20 | 21 | def self.get_access_token(request_token, params) 22 | request_token.get_access_token(oauth_verifier: params[:oauth_verifier]).token 23 | end 24 | end -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /app/controllers/stats_controller.rb: -------------------------------------------------------------------------------- 1 | class StatsController < ApplicationController 2 | def index 3 | respond_to do |format| 4 | format.json do 5 | render json: { 6 | users: User.where(active: true).count, 7 | pages: Page.count, 8 | page_histories: PageHistory.count, 9 | day_registers: User.where('created_at >= ?', 1.day.ago).count, 10 | day_users: User.where('actived_at >= ?', 1.day.ago).count, 11 | week_users: User.where('actived_at >= ?', 1.week.ago).count, 12 | month_users: User.where('actived_at >= ?', 1.month.ago).count, 13 | dropbox_users: DropboxUser.count, 14 | evernote_users: EvernoteUser.count 15 | } 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/user_message.rb: -------------------------------------------------------------------------------- 1 | class UserMessage < ActiveRecord::Base 2 | belongs_to :user 3 | belongs_to :sender_user, :class_name => 'User' 4 | belongs_to :page 5 | scope :user, -> u { where(user: u) } 6 | scope :page, -> p { where(page: p) } 7 | scope :type, -> t { where(message_type: t) } 8 | scope :read, -> p { where(read: true) } 9 | scope :unread, -> p { where(read: false) } 10 | validate :message_type, :inclusion => { :in => %w(free sidehelp) } 11 | 12 | def to_hash(*args) 13 | { 14 | user: user ? user.to_hash : nil, 15 | sender: sender_user ? sender_user.to_hash : nil, 16 | page: page ? page.to_hash : nil, 17 | title: title, 18 | body: body, 19 | read: read?, 20 | icon_url: icon_url 21 | } 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/support/wait_until.rb: -------------------------------------------------------------------------------- 1 | def wait_until(time=Capybara.default_wait_time) 2 | require "timeout" 3 | Timeout.timeout(time) do 4 | sleep(0.1) until value = yield 5 | value 6 | end 7 | end 8 | 9 | def wait_until_visible(selector, time=Capybara.default_wait_time) 10 | wait_until(time) do 11 | page.should have_selector(selector, visible: true) 12 | end 13 | end 14 | 15 | def wait_and_find_css(selector, time=Capybara.default_wait_time) 16 | wait_until(time) do 17 | page.has_css?(selector) 18 | end 19 | find(selector) 20 | end 21 | alias wait_and_find wait_and_find_css 22 | 23 | def wait_and_find_xpath(selector, time=Capybara.default_wait_time) 24 | wait_until(time) do 25 | page.has_xpath?(selector) 26 | end 27 | find(:xpath, selector) 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/ghauth_controller.rb: -------------------------------------------------------------------------------- 1 | require "#{Rails.root}/lib/ghauth" 2 | 3 | class GhauthController < ApplicationController 4 | rescue_from OAuth::Unauthorized, :with => Proc.new { redirect_to '/' } 5 | 6 | def sign_in 7 | redirect_to GhAuth.oauth.authorize_url(client_id: GhAuth.config['client_id'], scope: 'user:email') 8 | end 9 | 10 | def callback 11 | token = GhAuth.oauth.auth_code.get_token(params[:code]) 12 | logger.error token 13 | logger.error token.token 14 | user = GhUser.auth(token.token) 15 | session[:authorized_user_id] = user.id 16 | flash[:notice] = nil 17 | redirect_to :home 18 | rescue => e 19 | logger.error e.inspect 20 | flash[:notice] = I18n.t(:failed_facebook_login) 21 | redirect_to '/' 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /config/evernote-sample.yml: -------------------------------------------------------------------------------- 1 | development: 2 | consumer_key: XXXXXXX-0000 3 | consumer_secret: XXXXXXX 4 | sandbox: true 5 | callback: http://wripe.dev/evernote_auth/callback 6 | 7 | test: 8 | consumer_key: XXXXXXX-0000 9 | consumer_secret: XXXXXXX 10 | sandbox: true 11 | callback: http://127.0.0.1:57124/evernote_auth/callback 12 | tests: 13 | - email: wripe1@example.com 14 | password: wripe1 15 | account: wripe1 16 | access_token: "XXXXXXX" 17 | - email: wripe2@example.com 18 | password: wripe2 19 | account: wripe2 20 | access_token: "XXXXXXX" 21 | 22 | production: 23 | consumer_key: <%= ENV['EVERNOTE_KEY'] %> 24 | consumer_secret: <%= ENV['EVERNOTE_SECRET'] %> 25 | sandbox: false 26 | callback: <%= ENV['APP_BASE'] %>/evernote_auth/callback 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /app/views/feedbacks/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= paginate @feedbacks %> 2 | 3 | 4 | <% @feedbacks.each do |f|%> 5 | 6 | 16 | 17 | 18 | <% end %> 19 |
7 |
8 | <%= f.user.username rescue 'Guest' %> | 9 | <%= Feedback::SUBJECT_LABELS[f.subject] rescue '' %> | 10 | <%= raw "#{f.user.email}" rescue '' %> 12 |
13 |
<%= f.body %>
14 |
<%= f.user_agent %>
15 |
<%= link_to(image_tag(f.image.url, width:64, height: 64), f.image.url) if f.image.file? %>
20 | 21 | <%= paginate @feedbacks %> 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | /public/assets/ 8 | /public/system/ 9 | /solr 10 | /.vagrant 11 | /log/* 12 | *.dump 13 | 14 | # Ignore bundler config 15 | /.bundle 16 | vendor/bundle 17 | 18 | # Ignore the default SQLite database. 19 | /db/*.sqlite3 20 | 21 | # Ignore all logfiles and tempfiles. 22 | /log/*.log 23 | /log/*.lck 24 | /tmp 25 | /*.log 26 | 27 | .DS_Store 28 | 29 | # Ignore your setting files 30 | config/application.yml 31 | config/async.yml 32 | config/evernote.yml 33 | config/github.yml 34 | config/dropbox.yml 35 | config/facebook.yml 36 | config/paperclip_feedbacks_s3.yml 37 | -------------------------------------------------------------------------------- /app/models/gh_user.rb: -------------------------------------------------------------------------------- 1 | class GhUser < ActiveRecord::Base 2 | belongs_to :user 3 | 4 | def self.auth(ghtoken) 5 | gh = Octokit::Client.new(access_token: ghtoken) 6 | profile = gh.user 7 | ghid = profile['id'].to_s 8 | ghuser = GhUser.where(:ghid => ghid).first 9 | unless ghuser 10 | ghuser = GhUser.create! :token => ghtoken, :ghid => ghid, :json => JSON.generate(profile) 11 | end 12 | user = ghuser.user 13 | unless user 14 | username = profile['login'] 15 | while User.where(:username => username).count > 0 16 | username += rand(10).to_s 17 | end 18 | user = User.create! :username => username 19 | ghuser.update_attributes :user_id => user.id 20 | end 21 | 22 | user.update_attributes :icon_url => profile['avatar_url'] 23 | email = gh.emails.first rescue nil 24 | user.update_attributes :email => email unless email.blank? 25 | user 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/assets/stylesheets/app/settings.scss: -------------------------------------------------------------------------------- 1 | $icon_size: 128px; 2 | 3 | #settings-backup { 4 | .dropbox-icon128 { 5 | position: absolute; 6 | left: 15px; 7 | top: 15px; 8 | width: $icon_size; 9 | height: $icon_size; 10 | padding: 0 !important; 11 | margin: 0 !important; 12 | background-image: url('/images/dropbox_icon128.png'); 13 | } 14 | } 15 | 16 | #settings-evernote { 17 | .evernote-icon128 { 18 | position: absolute; 19 | left: 15px; 20 | top: 15px; 21 | width: $icon_size; 22 | height: $icon_size; 23 | padding: 0 !important; 24 | margin: 0 !important; 25 | background-image: url('/images/evernote_icon128.png'); 26 | } 27 | } 28 | 29 | .desktop, .tablet { 30 | #settings-backup, #settings-evernote { 31 | .messages { 32 | padding: 0 !important; 33 | margin: 0 0 0 $icon_size + 15px !important; 34 | min-height: $icon_size; 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/page_search_panel.js.coffee: -------------------------------------------------------------------------------- 1 | #= require app/page_list_panel 2 | 3 | class PageSearchPanel extends PageListPanel 4 | constructor: (tab_el, tab_name) -> 5 | super(tab_el, tab_name, '', '/pages/search.json?q=') 6 | @search_box_el = $('#list-page-searchbox') 7 | @search_query_el = $('#list-page-search-query') 8 | @empty_message_el = $('#list-page-search-empty') 9 | @search_box_el.on 'submit', (e) => 10 | e.preventDefault() 11 | @load() 12 | 13 | activate: -> 14 | @search_box_el.show() 15 | delay 100, => 16 | @search_query_el.focus() 17 | super() 18 | 19 | deactivate: -> 20 | @search_box_el.hide() 21 | super() 22 | 23 | load: (collection) -> 24 | unless @search_query_el.val() == '' 25 | collection = new PageCollection(@root_url+encodeURI(@search_query_el.val())) unless collection 26 | super(collection) 27 | 28 | 29 | window.PageSearchPanel = PageSearchPanel -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | */ 12 | 13 | .none { 14 | display: none; 15 | } 16 | 17 | body { 18 | height: 100%; 19 | overflow: auto !important; 20 | } 21 | 22 | body.phone { 23 | margin: 0 !important; 24 | padding: 0 !important; 25 | } 26 | 27 | form { 28 | margin: 0 !important; 29 | } 30 | 31 | .navbar-fixed-top { 32 | position: absolute; 33 | left: 0; 34 | top: 0; 35 | z-index: 32768; 36 | } 37 | -------------------------------------------------------------------------------- /app/models/fb_user.rb: -------------------------------------------------------------------------------- 1 | class FbUser < ActiveRecord::Base 2 | belongs_to :user 3 | 4 | def self.auth(fbtoken) 5 | graph = Koala::Facebook::API.new(fbtoken) 6 | fb_profile = graph.get_object("me") 7 | fbid = fb_profile['id'] 8 | fbuser = FbUser.find_by_fbid(fbid) 9 | unless fbuser 10 | fbuser = FbUser.create :token => fbtoken, :fbid => fbid, :json => JSON.generate(fb_profile) 11 | end 12 | user = fbuser.user 13 | unless user 14 | username = fb_profile['username'] || fb_profile['first_name'].gsub(/\W/,'')[0,14].downcase 15 | while User.where(:username => username).count > 0 16 | username += rand(10).to_s 17 | end 18 | user = User.create :username => username 19 | fbuser.update_attributes :user_id => user.id 20 | end 21 | user.update_attributes :icon_url => graph.get_picture(fb_profile['id']) 22 | user.update_attributes :email => fb_profile['email'] if user.email.blank? 23 | user 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/block_profiler.rb: -------------------------------------------------------------------------------- 1 | class BlockProfiler 2 | if %w(development test).include?(Rails.env) 3 | def self.measure(file_name='prof') 4 | RubyProf.start 5 | yield 6 | results = RubyProf.stop 7 | 8 | FileUtils.mkdir_p "#{Rails.root}/tmp/performance" 9 | # Print a flat profile to text 10 | File.open "#{Rails.root}/tmp/performance/#{file_name}-graph.html", 'w' do |file| 11 | RubyProf::GraphHtmlPrinter.new(results).print(file) 12 | end 13 | 14 | File.open "#{Rails.root}/tmp/performance/#{file_name}-flat.txt", 'w' do |file| 15 | # RubyProf::FlatPrinter.new(results).print(file) 16 | RubyProf::FlatPrinterWithLineNumbers.new(results).print(file) 17 | end 18 | 19 | File.open "#{Rails.root}/tmp/performance/#{file_name}-stack.html", 'w' do |file| 20 | RubyProf::CallStackPrinter.new(results).print(file) 21 | end 22 | end 23 | else 24 | def self.measure(file_name='prof') 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | require 'sunspot/solr/railtie' 7 | 8 | Wripe::Application.load_tasks 9 | 10 | desc 'deploy to heroku' 11 | task :deploy do 12 | sh 'git push heroku master' 13 | end 14 | 15 | desc 'backup' 16 | task :backup => [:environment] do 17 | require "#{Rails.root}/lib/backup_dropbox" 18 | DropboxBackup.backups 19 | end 20 | 21 | desc 'queue runner' 22 | task :queue_runner => [:environment] do 23 | # load for ActiveRecord::ConnectionAdapters::TransactionState 24 | ActiveRecord::ConnectionAdapters::ClosedTransaction 25 | ActiveRecordAsync.runner 26 | end 27 | 28 | desc 'test on browsers' 29 | task :browsers do 30 | %w(chrome safari firefox phone tablet).each do |driver| 31 | #%w(chrome safari firefox ie).each do |driver| 32 | sh "rake DRIVER=#{driver}" 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(:default, Rails.env) 8 | 9 | module Wripe 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | # config.time_zone = 'Central Time (US & Canada)' 18 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 21 | # config.i18n.default_locale = :de 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/assets/stylesheets/layout.scss: -------------------------------------------------------------------------------- 1 | $body_bg_color: white; 2 | $main_bg_color: #F3F3F3; 3 | 4 | // 5 | $nav_top_bg_color: transparent; 6 | $nav_top_text_color: #333; 7 | $nav_top_height: 0;//32px; 8 | 9 | $nav_left_width: 90px; 10 | $nav_left_width_phone: 54px; 11 | 12 | $nav_left_bg_color: #272D33; 13 | $nav_left_icon_color: #5D5D5C; 14 | $nav_left_text_color: rgba(255, 255, 255, 0.5); 15 | 16 | $nav_left_active_bg_color: #f36c3d; 17 | $nav_left_active_text_color: white; 18 | $nav_left_active_icon_color: white; 19 | 20 | $edit_page_sidebar_bg_color: white; 21 | $edit_page_sidebar_width: 320px; 22 | 23 | $list_page_cursor_color: #f36c3d; 24 | 25 | $list_page_sidebar_bg_color: $main_bg_color; 26 | $list_page_sidebar_width: 240px; 27 | $sidebar_top: 48px; 28 | 29 | $panel_margin: 8px; 30 | 31 | $help_bg_color: white; 32 | $help_text_color: #5D5D5C; 33 | $help_title_bg_color: white; 34 | $help_title_text_color: #5D5D5C; 35 | 36 | $editor_font_family: arial,helvetica,clean,sans-serif; 37 | $editor_font_family_fixed: Consolas, 'Courier New', Courier, Monaco, monospace; 38 | 39 | $feedback_text_color: white; 40 | $feedback_tab_color: #f36c3d; 41 | -------------------------------------------------------------------------------- /spec/features/signin_facebook_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | def signout_facebook 4 | visit 'https://www.facebook.com/' 5 | if page.has_css?('#userNavigationLabel') 6 | find('#userNavigationLabel').click 7 | find("#logout_form input[type='submit']").click 8 | wait_until do # until back to top 9 | page.has_css?('#email') 10 | end 11 | end 12 | end 13 | 14 | feature 'Facebook', :js => true do 15 | scenario 'signin' do 16 | signout_facebook 17 | 18 | visit '/' 19 | find('#signin-facebook').click 20 | 21 | wait_until do 22 | page.has_xpath?("//input[@name='email']") 23 | end 24 | 25 | test_user = FbAuth.config['tests'].first 26 | find(:xpath, "//input[@name='email']").set test_user['email'] 27 | find(:xpath, "//input[@name='pass']").set test_user['password'] 28 | find(:xpath, "//input[@name='login']|//button[@name='login']").click 29 | 30 | wait_until_visible('#nav-username-link') 31 | 32 | current_path.should.should == '/app' 33 | evaluate_script('session.username()').should == test_user['account'] 34 | user = User.find_by_email test_user['email'] 35 | user.pages.should be_empty 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /app/controllers/settings_controller.rb: -------------------------------------------------------------------------------- 1 | class SettingsController < ApplicationController 2 | before_filter :required_login 3 | 4 | def show 5 | respond_to do |format| 6 | format.html 7 | format.json do 8 | render json: { 9 | use_dropbox: !!current_user.dropbox_user, 10 | use_evernote: !!current_user.evernote_user 11 | } 12 | end 13 | end 14 | end 15 | 16 | def update 17 | if params[:use_dropbox] == 'false' 18 | current_user.dropbox_user.destroy if current_user.dropbox_user 19 | end 20 | 21 | if params[:use_evernote] == 'false' 22 | current_user.evernote_user.destroy if current_user.evernote_user 23 | session[:enconnect] = nil 24 | session[:evernote_token] = nil 25 | end 26 | 27 | unless params[:autosave].nil? 28 | current_user.properties[:autosave] = (params[:autosave] == 'true') 29 | end 30 | 31 | respond_to do |format| 32 | format.html { redirect_to :show } 33 | format.json do 34 | render json: { 35 | use_dropbox: !!current_user.dropbox_user, 36 | use_evernote: !!current_user.evernote_user 37 | } 38 | end 39 | end 40 | end # update 41 | 42 | end 43 | -------------------------------------------------------------------------------- /spec/features/create_new_page_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | feature 'create new note', :js => true do 4 | scenario 'create note' do 5 | user = FactoryGirl.create(:testdrive1) 6 | test_login 'testdrive1' 7 | 8 | find('#navigator-new').click 9 | wait_until_visible $el[:edit_body] 10 | 11 | # create new note 12 | find($el[:edit_title]).set "TEST NOTE" 13 | find($el[:edit_body]).set "TEST\n123\n" 14 | find($el[:edit_save]).click 15 | 16 | sleep 0.5 17 | wait_until_visible "#{$el[:edit_save]} span[name='save']" 18 | 19 | user.pages.count.should == 1 20 | user.pages.first.lock_version.should == 0 21 | user.pages.first.title.should == "TEST NOTE" 22 | user.pages.first.body.should == "TEST\n123\n" 23 | 24 | # modify 25 | find($el[:edit_title]).set "TEST NOTE2" 26 | find($el[:edit_body]).set "TEST\nABC\n" 27 | find($el[:edit_save]).click 28 | 29 | sleep 0.5 30 | wait_until_visible "#{$el[:edit_save]} span[name='save']" 31 | 32 | user.pages.count.should == 1 33 | user.pages.first.lock_version.should == 1 34 | user.pages.first.title.should == "TEST NOTE2" 35 | user.pages.first.body.should == "TEST\nABC\n" 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /app/controllers/evernote_auth_controller.rb: -------------------------------------------------------------------------------- 1 | class EvernoteAuthController < ApplicationController 2 | rescue_from OAuth::Unauthorized, :with => Proc.new { redirect_to '/' } 3 | 4 | def sign_in 5 | request_token = EvernoteAuth.request_token 6 | session[:evernote_rt] = request_token 7 | redirect_to request_token.authorize_url 8 | end 9 | 10 | def connect 11 | session[:enconnect] = true 12 | sign_in 13 | end 14 | 15 | def callback 16 | access_token = EvernoteAuth.get_access_token(session[:evernote_rt], params) 17 | 18 | if current_user && session[:enconnect] 19 | begin 20 | user = EvernoteUser.connect(access_token, current_user) 21 | rescue EvernoteUser::AlreadyConnected => e 22 | session[:evernote_token] = access_token 23 | @error = :already_connected 24 | end 25 | render layout: false 26 | else 27 | # sign in 28 | user = EvernoteUser.auth(access_token) 29 | session[:authorized_user_id] = user.id 30 | redirect_to :home 31 | end 32 | session[:enconnect] = nil 33 | 34 | rescue => e 35 | logger.error e.inspect 36 | logger.error e.backtrace.join(", ") 37 | flash[:notice] = I18n.t(:failed_facebook_login) 38 | redirect_to '/' 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Wripe::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | app_base = URI(ENV['APP_BASE'] || 'https://wri.pe') 4 | Rails.application.routes.default_url_options = { 5 | protocol: app_base.scheme, 6 | host: app_base.host 7 | } 8 | 9 | # In the development environment your application's code is reloaded on 10 | # every request. This slows down response time but is perfect for development 11 | # since you don't have to restart the web server when you make code changes. 12 | config.cache_classes = false 13 | 14 | # Do not eager load code on boot. 15 | config.eager_load = false 16 | 17 | # Show full error reports and disable caching. 18 | config.consider_all_requests_local = true 19 | config.action_controller.perform_caching = false 20 | 21 | # Don't care if the mailer can't send. 22 | config.action_mailer.raise_delivery_errors = false 23 | 24 | # Print deprecation notices to the Rails logger. 25 | config.active_support.deprecation = :log 26 | 27 | # Raise an error on page load if there are pending migrations 28 | config.active_record.migration_error = :page_load 29 | 30 | # Debug mode disables concatenation and preprocessing of assets. 31 | config.assets.debug = true 32 | end 33 | -------------------------------------------------------------------------------- /spec/features/signin_github_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | def signout_github 4 | visit 'https://github.com/logout' 5 | if page.has_css?('.auth-form-body .button') 6 | find('.auth-form-body .button').click 7 | wait_until(10) do 8 | current_path == '/' 9 | end 10 | end 11 | end 12 | 13 | feature 'GitHub', :js => true do 14 | scenario 'signin' do 15 | if %i(safari firefox selenium).include?(Capybara.javascript_driver) 16 | pending "#{Capybara.javascript_driver} doesn't support this test" 17 | else 18 | signout_github 19 | 20 | visit '/' 21 | find('#signin-github').click 22 | 23 | wait_until do 24 | page.has_xpath?("//input[@name='login_field']|//input[@name='login']") 25 | end 26 | 27 | test_user = GhAuth.config['tests'].first 28 | find(:xpath, "//input[@name='login_field']|//input[@name='login']").set test_user['email'] 29 | find(:xpath, "//input[@name='password']|//input[@name='password']").set test_user['password'] 30 | find(:xpath, "//input[@name='commit']|//button[@type='submit']").click 31 | 32 | wait_until_visible('#nav-username-link') 33 | 34 | current_path.should == '/app' 35 | evaluate_script('session.username()').should == test_user['account'] 36 | user = User.find_by_email test_user['email'] 37 | user.pages.should be_empty 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/panel.js.coffee: -------------------------------------------------------------------------------- 1 | #= require shared/underscore 2 | #= require shared/backbone 3 | #= require shared/defer 4 | #= require shared/bootstrap 5 | 6 | class AbsolutePanel 7 | constructor: (klass) -> 8 | _.extend @, Backbone.Events 9 | @compiled_template = {} 10 | 11 | if klass && klass.el 12 | for name, selector of klass.el 13 | @["#{name}_el"] = $(selector) 14 | 15 | activate: -> 16 | Deferred (defer) => 17 | defer.resolve() 18 | 19 | deactivate: -> 20 | Deferred (defer) => 21 | defer.resolve() 22 | 23 | reactivate: -> 24 | @activate() 25 | 26 | resize: -> 27 | 28 | hotkeys: (ev) -> 29 | 30 | # protected 31 | template: (name, obj) -> 32 | unless @compiled_template[name] 33 | @compiled_template[name] = _.template($("#"+name).html()) 34 | @compiled_template[name](obj) 35 | 36 | full_height: (el, bottom, height_el, recur) -> 37 | height_el = el unless height_el 38 | offset = height_el.offset() 39 | h = (window_height() - offset.top - (bottom || 0)) 40 | el.height(h) 41 | unless recur 42 | delay 100, => 43 | if el.height() != h 44 | @full_height(el, bottom, height_el, true) 45 | 46 | _.templateSettings = { 47 | evaluate : /{%([\s\S]+?)%}/g, 48 | interpolate : /\${raw ([\s\S]+?)}/g, 49 | escape : /\${([\s\S]+?)}/g 50 | }; 51 | 52 | window.AbsolutePanel = AbsolutePanel 53 | -------------------------------------------------------------------------------- /app/controllers/pages/members_controller.rb: -------------------------------------------------------------------------------- 1 | class Pages::MembersController < ApplicationController 2 | before_filter :required_login 3 | before_filter :prepage_variants 4 | 5 | def index 6 | @page_users = @page.page_users 7 | render_page_user 8 | end 9 | 10 | def create 11 | @page_user = @page.page_users.create params[:user] 12 | render_page_user 13 | end 14 | 15 | def show 16 | @page_user = @page.page_users.where(params[:id]).first 17 | render_page_user 18 | end 19 | 20 | def update 21 | @page_user = @page.page_users.where(params[:id]).first 22 | @page_user.update_attributes params[:permission] 23 | render_page_user 24 | end 25 | 26 | def destroy 27 | @page.page_users.where(params[:user][:id]).first.destroy 28 | head 200 29 | end 30 | 31 | private 32 | def render_page_user 33 | respond_to do |format| 34 | format.html 35 | format.json { 36 | if @page_users 37 | render :json => { :owner => @page.user.to_hash, :users => @page_users.map(&:to_hash) } 38 | else 39 | render :json => @page_user.to_hash 40 | end 41 | } 42 | end 43 | end 44 | 45 | private 46 | def forbidden 47 | head 403 48 | end 49 | 50 | private 51 | def prepage_variants 52 | @page = Page.find_by_key(params[:page_id]) 53 | return forbidden unless @page.permit_share?(current_user) 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /app/assets/javascripts/shared/modal_dialog.js.coffee: -------------------------------------------------------------------------------- 1 | #= require shared/underscore 2 | #= require shared/backbone 3 | 4 | 5 | class ModalDialog 6 | constructor: (options) -> 7 | _.extend @, Backbone.Events 8 | options = { show: false } unless options 9 | @el.unbind 'show' 10 | @el.on 'show', => 11 | ModalDialog.modal_counter += 1 12 | @shown() 13 | @el.unbind 'hide' 14 | @el.on 'hide', (e) => 15 | ModalDialog.modal_counter -= 1 16 | @hidden() 17 | if @defer 18 | @defer.reject() 19 | @defer = undefined 20 | 21 | actions = $("*[data-action]", @el) 22 | actions.unbind('click') 23 | actions.click (ev) => 24 | ev.preventDefault() 25 | if @defer 26 | action_name = $(ev.currentTarget).attr('data-action') 27 | @action(action_name) 28 | @defer.resolve(action_name) 29 | @defer = undefined 30 | @hide() 31 | 32 | @el.modal(options) 33 | 34 | @modal_counter: 0 35 | 36 | @is_active: -> 37 | @modal_counter > 0 38 | 39 | show: -> 40 | Deferred (@defer) => 41 | @will_show() 42 | @el.modal('show') 43 | 44 | hide: -> 45 | @will_hide() 46 | @el.modal('hide') 47 | 48 | action: (name) -> 49 | # please override 50 | 51 | will_show: -> 52 | # please override 53 | 54 | shown: -> 55 | # please override 56 | 57 | will_hide: -> 58 | # please override 59 | 60 | hidden: -> 61 | # please override 62 | 63 | 64 | window.ModalDialog = ModalDialog 65 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | around_filter :active_record_queue_filter if !Settings.flags.disable_ar_async 4 | 5 | private 6 | def required_login 7 | unless current_user 8 | respond_to do |format| 9 | format.html { redirect_to '/' } 10 | format.json { head 401 } 11 | end 12 | end 13 | true 14 | end 15 | 16 | private 17 | def required_admin 18 | return true if Rails.env == 'development' 19 | unless current_user && current_user.is_admin? 20 | respond_to do |format| 21 | format.html { head 404 } 22 | end 23 | end 24 | true 25 | end 26 | 27 | private 28 | def current_user 29 | return @current_user if @current_user 30 | return nil if @current_user === false 31 | user = User.where(:id => session[:authorized_user_id]).first 32 | @current_user = user && user.active? ? user : false 33 | end 34 | 35 | private 36 | def active_record_queue_filter 37 | ActiveRecordAsync.async_batch do 38 | yield 39 | end 40 | end 41 | 42 | private 43 | def handle_unverified_request 44 | # reset_session 45 | raise ActionController::InvalidAuthenticityToken 46 | end 47 | 48 | rescue_from ActionController::InvalidAuthenticityToken do |exception| 49 | respond_to do |format| 50 | format.html do 51 | redirect_to '/' 52 | end 53 | format.json do 54 | head :precondition_failed 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/relative_date.js: -------------------------------------------------------------------------------- 1 | var relativeDate = (function(undefined){ 2 | 3 | var SECOND = 1000, 4 | MINUTE = 60 * SECOND, 5 | HOUR = 60 * MINUTE, 6 | DAY = 24 * HOUR, 7 | WEEK = 7 * DAY, 8 | YEAR = DAY * 365, 9 | MONTH = YEAR / 12; 10 | 11 | var formats = [ 12 | [ 0.7 * MINUTE, 'just now' ], 13 | [ 1.5 * MINUTE, 'a minute ago' ], 14 | [ 60 * MINUTE, 'minutes ago', MINUTE ], 15 | [ 1.5 * HOUR, 'an hour ago' ], 16 | [ DAY, 'hours ago', HOUR ], 17 | [ 2 * DAY, 'yesterday' ], 18 | [ 7 * DAY, 'days ago', DAY ], 19 | [ 1.5 * WEEK, 'a week ago'], 20 | [ MONTH, 'weeks ago', WEEK ], 21 | [ 1.5 * MONTH, 'a month ago' ], 22 | [ YEAR, 'months ago', MONTH ], 23 | [ 1.5 * YEAR, 'a year ago' ], 24 | [ Number.MAX_VALUE, 'years ago', YEAR ] 25 | ]; 26 | 27 | function relativeDate(input,reference){ 28 | !reference && ( reference = (new Date).getTime() ); 29 | reference instanceof Date && ( reference = reference.getTime() ); 30 | input instanceof Date && ( input = input.getTime() ); 31 | 32 | var delta = reference - input, 33 | format, i, len; 34 | 35 | for(i = -1, len=formats.length; ++i < len; ){ 36 | format = formats[i]; 37 | if(delta < format[0]){ 38 | return format[2] == undefined ? format[1] : Math.round(delta/format[2]) + ' ' + format[1]; 39 | } 40 | }; 41 | } 42 | 43 | return relativeDate; 44 | 45 | })(); 46 | 47 | if(typeof module != 'undefined' && module.exports){ 48 | module.exports = relativeDate; 49 | } 50 | 51 | if(typeof window != 'undefined'){ 52 | window.relativeDate = relativeDate; 53 | } 54 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | before_filter :required_login, only: [:show] 3 | 4 | def show 5 | current_user.helps.clear(:mobile) if request.user_agent && request.user_agent.downcase.include?('mobile') 6 | current_user.update_attribute :actived_at, Time.now 7 | 8 | show_updates = false 9 | if (params[:version] || 0).to_i == WRIPE_VERSION && (current_user.properties['version'] || 0).to_i < WRIPE_VERSION 10 | current_user.properties['version'] = WRIPE_VERSION 11 | show_updates = true 12 | end 13 | 14 | a = current_user.properties.where(:key => %w(export-key autosave)).map{|p| [p.key, p.value]} 15 | props = Hash[a] 16 | %w(autosave).each do |key| 17 | if props[key] 18 | props[key] = %w(t true y yes).include?(props[key].downcase) 19 | end 20 | end 21 | 22 | render json: { 23 | user: current_user.to_hash, 24 | pages_count: current_user.pages.count, 25 | csrf_param: request_forgery_protection_token, 26 | csrf_token: form_authenticity_token, 27 | helps: current_user.helps.map(&:to_hash), 28 | properties: props, 29 | show_updates: show_updates 30 | } 31 | end 32 | 33 | def destroy 34 | reset_session 35 | render :layout => false 36 | end 37 | 38 | if %w(test development).include?(Rails.env) 39 | def test 40 | if params[:username] 41 | session[:authorized_user_id] = User.find_by_username(params[:username]).id 42 | flash[:notice] = nil 43 | redirect_to :home 44 | else 45 | render :layout => false 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pages.scss: -------------------------------------------------------------------------------- 1 | @import "layout"; 2 | 3 | #side-tab-content { 4 | overflow:hidden; 5 | } 6 | 7 | html, body { 8 | height: 100%; 9 | background: $body_bg_color; 10 | overflow-y: hidden; 11 | overflow-x: hidden; 12 | } 13 | 14 | .container-fluid { 15 | padding-top: 32px; 16 | height: 100%; 17 | } 18 | 19 | .phone { 20 | #side-tab-content { 21 | margin-left: -6px !important; 22 | margin-right: -6px !important; 23 | padding-left: 6px; 24 | padding-right: 6px; 25 | padding-bottom: 6px; 26 | } 27 | } 28 | 29 | .row-fluid { 30 | height: 100%; 31 | } 32 | 33 | .row-fluid .span8 { 34 | height: 100%; 35 | } 36 | 37 | .row-fluid .span4 { 38 | height: 100%; 39 | padding: 8px 0; 40 | } 41 | 42 | #page_title { 43 | width: 100%; 44 | box-sizing: border-box; 45 | -moz-box-sizing: border-box; 46 | font-size: 1.2em; 47 | height: 2em; 48 | margin: 8px 0; 49 | } 50 | 51 | #page_body { 52 | width: 100%; 53 | box-sizing: border-box; 54 | -moz-box-sizing: border-box; 55 | } 56 | 57 | .float-right { 58 | float: right; 59 | } 60 | 61 | form { 62 | height: 100%; 63 | } 64 | 65 | iframe.tab-pane { 66 | border: 0; 67 | width: 100%; 68 | } 69 | 70 | #side-tab-content { 71 | background-color: #f8f8f8; 72 | border-bottom: 1px solid #ddd; 73 | border-left: 1px solid #ddd; 74 | border-right: 1px solid #ddd; 75 | } 76 | 77 | .side-nav { 78 | margin-bottom: 0; 79 | } 80 | 81 | .nav-tabs > .active > a { 82 | background-color: #f8f8f8 !important; 83 | } 84 | 85 | .line-selectbox { 86 | line-height: 32px; 87 | } 88 | 89 | #confirm-delete-btn { 90 | position: absolute; 91 | bottom: 12px; 92 | right: 24px; 93 | } 94 | -------------------------------------------------------------------------------- /app/views/app/_markdown_toolbar.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
H1
8 |
H2
9 |
H3
10 |
H4
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
Today
19 |
20 |
21 | -------------------------------------------------------------------------------- /app/assets/javascripts/shared/feedback.js.coffee: -------------------------------------------------------------------------------- 1 | #= require shared/html2canvas 2 | #= require shared/modal_dialog 3 | 4 | 5 | class FeedbackDialog extends ModalDialog 6 | el: $('#feedback-dialog') 7 | constructor: -> 8 | super() 9 | 10 | action: (action) -> 11 | if action == 'send' 12 | data = 13 | feedback: 14 | subject: $("#feedback-subject").val() 15 | body: $("#feedback-body").val() 16 | if $("#feedback-attach").is(':checked') 17 | data.feedback.image_data = $("#feedback-image-data").val() 18 | 19 | defer = authorizedRequest 20 | url: '/feedbacks.json' 21 | method: 'POST' 22 | dataType: 'json' 23 | data: data 24 | defer.fail => 25 | $.bootstrapGrowl("Failed to post feedback", {type: 'error'}); 26 | defer.done => 27 | $.bootstrapGrowl("Thank you for your feedback", {type: 'success'}); 28 | $("#feedback-body").val('') 29 | 30 | 31 | openFeedback = -> 32 | try 33 | html2canvas [document.body], 34 | onrendered: (canvas) -> 35 | dialog = new FeedbackDialog() 36 | dialog.show() 37 | $("#feedback-image-data").val(canvas.toDataURL()) 38 | $("#feedback-capture").attr('src', canvas.toDataURL()) 39 | $("#feedback-capture").css('width', $("#feedback-capture").height() * (canvas.width/canvas.height) ) 40 | catch e 41 | $("#feedback-attach-capture").hide() 42 | dialog = new FeedbackDialog() 43 | dialog.show() 44 | 45 | 46 | $ () -> 47 | $("#feedback-tab").click -> 48 | openFeedback() 49 | $("#settings-feedback-button").click -> 50 | openFeedback() 51 | $("#help-feedback-button").click -> 52 | openFeedback() 53 | -------------------------------------------------------------------------------- /app/assets/stylesheets/app/user_messages.scss: -------------------------------------------------------------------------------- 1 | #user_messages { 2 | a { 3 | color: inherit; 4 | text-decoration: none; 5 | } 6 | 7 | .subject { 8 | white-space: nowrap; 9 | overflow: hidden; 10 | text-overflow: ellipsis; 11 | } 12 | 13 | .body { 14 | font-size: 12px; 15 | opacity: 0.5; 16 | white-space: nowrap; 17 | overflow: hidden; 18 | text-overflow: ellipsis; 19 | } 20 | 21 | .alert { 22 | padding: 4px 8px; 23 | margin: 8px 0; 24 | } 25 | 26 | .close { 27 | right: 0!important; 28 | } 29 | } 30 | 31 | /* 32 | 33 |
34 |
35 | 36 | 37 |
Request Request Request Request
38 |
39 | Best check yo self, you're not... 40 |
41 |
42 |
43 | 44 |
45 | 46 |
Request
47 |
48 | Best check yo self, you're not... 49 |
50 |
51 |
52 | 53 | 54 | 67 | */ -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/pickup_dates.js: -------------------------------------------------------------------------------- 1 | var pickup_dates = (function() { 2 | var date_matcher = (function() { 3 | // 2012-12-31 - 12/31/2012 4 | var year_match = "20[0-9]{2}"; 5 | var month_match = "[0-1]{0,1}[0-9]"; 6 | var day_match = "[0-3]{0,1}[0-9]"; 7 | var sep1 = "[.-/]"; 8 | var pattern11 = [year_match, month_match, day_match].join(sep1); 9 | var pattern12 = [month_match, day_match, year_match].join(sep1); 10 | var pattern13 = [day_match, month_match, year_match].join(sep1); 11 | 12 | // February 20th 2013 / 1st June 2000 13 | var text_month_match = "(january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)"; 14 | var text_day_match = "[0-3]{0,1}[0-9]{1}(st|nd|rd|th|)"; 15 | var sep2 = "[, ]{1,3}"; 16 | var pattern21 = [text_month_match, text_day_match, year_match].join(sep2); 17 | var pattern22 = [text_day_match, text_month_match, year_match].join(sep2); 18 | 19 | return "[^-.\/0-9](" + [pattern11, pattern12, pattern13, pattern21, pattern22].join('|') + ")[^-.\/0-9]"; 20 | })(); 21 | var timezone = (new Date).getTimezoneOffset() * 60000; 22 | 23 | return function(text) { 24 | text = ' ' + text + ' '; 25 | var dates = []; 26 | var match = true; 27 | while(match) { 28 | match = new RegExp(date_matcher, 'ig').exec(text) 29 | if(match) { 30 | var date = Date.parse(match[1].replace(/-/g,'/').replace(/(st|rd|th)/g,'')); 31 | if(date) { 32 | dates.push((date - timezone) / 1000); 33 | } 34 | text = ' ' + text.substring(match.index + match[0].length); 35 | } 36 | } 37 | return dates 38 | }; 39 | })(); 40 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/shared/patch.scss: -------------------------------------------------------------------------------- 1 | @import "layout"; 2 | 3 | html, body { 4 | width: 100%; 5 | height: 100%; 6 | } 7 | 8 | body form { 9 | margin: 0; 10 | } 11 | 12 | a { 13 | color: #2980b9; 14 | } 15 | 16 | .circle { 17 | display: block; 18 | width: 24px; 19 | height: 24px; 20 | font-size: 16px; 21 | line-height: 24px; 22 | text-align: center; 23 | border-radius: 12px; 24 | background-color: #EA5444; 25 | color: white; 26 | 27 | } 28 | 29 | .btn { 30 | background-image: none !important; 31 | text-shadow: none !important; 32 | border: 0 !important; 33 | box-shadow: none!important; 34 | background-color: #95a5a6 !important; 35 | color: white !important; 36 | } 37 | 38 | .btn.disabled { 39 | opacity: 0.5; 40 | } 41 | 42 | .btn-success { 43 | background-color: #27ae60 !important; 44 | } 45 | 46 | .btn-primary { 47 | background-color: #2980b9 !important; 48 | } 49 | 50 | .btn-danger { 51 | background-color: #c0392b !important; 52 | } 53 | 54 | .modal-footer { 55 | border-top: none; 56 | } 57 | 58 | .space { 59 | background: rgba(0.5, 0.5, 0.5, 0.05); 60 | border-bottom: 1px solid rgba(0.5, 0.5, 0.5, 0.1); 61 | margin: 0 1px; 62 | padding: 0 2px; 63 | } 64 | 65 | .phone { 66 | .navbar-fixed-top { 67 | position: absolute!important; 68 | } 69 | .modal { 70 | margin: 0px!important; 71 | width: 90% !important; 72 | left: 5% !important; 73 | } 74 | 75 | .container-fluid { 76 | padding-left: 4px !important; 77 | padding-right: 4px !important; 78 | } 79 | 80 | .modal-header, .modal-body, .modal-footer { 81 | padding: 8px 12px !important; 82 | } 83 | } 84 | 85 | .tablet { 86 | .navbar-fixed-top { 87 | position: absolute!important; 88 | } 89 | } -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/html2canvas/Renderer.js: -------------------------------------------------------------------------------- 1 | _html2canvas.Renderer = function(parseQueue, options){ 2 | 3 | function createRenderQueue(parseQueue) { 4 | var queue = []; 5 | 6 | var sortZ = function(zStack){ 7 | var subStacks = [], 8 | stackValues = []; 9 | 10 | zStack.children.forEach(function(stackChild) { 11 | if (stackChild.children && stackChild.children.length > 0){ 12 | subStacks.push(stackChild); 13 | stackValues.push(stackChild.zindex); 14 | } else { 15 | queue.push(stackChild); 16 | } 17 | }); 18 | 19 | stackValues.sort(function(a, b) { 20 | return a - b; 21 | }); 22 | 23 | stackValues.forEach(function(zValue) { 24 | var index; 25 | 26 | subStacks.some(function(stack, i){ 27 | index = i; 28 | return (stack.zindex === zValue); 29 | }); 30 | sortZ(subStacks.splice(index, 1)[0]); 31 | 32 | }); 33 | }; 34 | 35 | sortZ(parseQueue.zIndex); 36 | 37 | return queue; 38 | } 39 | 40 | function getRenderer(rendererName) { 41 | var renderer; 42 | 43 | if (typeof options.renderer === "string" && _html2canvas.Renderer[rendererName] !== undefined) { 44 | renderer = _html2canvas.Renderer[rendererName](options); 45 | } else if (typeof rendererName === "function") { 46 | renderer = rendererName(options); 47 | } else { 48 | throw new Error("Unknown renderer"); 49 | } 50 | 51 | if ( typeof renderer !== "function" ) { 52 | throw new Error("Invalid renderer defined"); 53 | } 54 | return renderer; 55 | } 56 | 57 | return getRenderer(options.renderer)(parseQueue, options, document, createRenderQueue(parseQueue), _html2canvas); 58 | }; 59 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Wripe::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | Rails.application.routes.default_url_options = { 4 | host: 'localhost:57124', 5 | } 6 | 7 | # The test environment is used exclusively to run your application's 8 | # test suite. You never need to work with it otherwise. Remember that 9 | # your test database is "scratch space" for the test suite and is wiped 10 | # and recreated between test runs. Don't rely on the data there! 11 | config.cache_classes = true 12 | 13 | # Do not eager load code on boot. This avoids loading your whole application 14 | # just for the purpose of running a single test. If you are using a tool that 15 | # preloads Rails for running tests, you may have to set it to true. 16 | config.eager_load = true 17 | 18 | # Configure static asset server for tests with Cache-Control for performance. 19 | config.serve_static_assets = true 20 | config.static_cache_control = "public, max-age=3600" 21 | 22 | # Show full error reports and disable caching. 23 | config.consider_all_requests_local = true 24 | config.action_controller.perform_caching = false 25 | 26 | # Raise exceptions instead of rendering exception templates. 27 | config.action_dispatch.show_exceptions = false 28 | 29 | # Disable request forgery protection in test environment. 30 | config.action_controller.allow_forgery_protection = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | end 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wri.pe 2 | 3 | https://wri.pe source code. 4 | 5 | ## Install 6 | 7 | ### requirements 8 | 9 | - Ruby 2.0.0 and above 10 | - Bundler 11 | - JRE 1.6 and above (for Solr) 12 | - Pow - http://pow.cx/ 13 | 14 | ### optional requirenments 15 | 16 | - Powder - https://github.com/rodreegez/powder 17 | 18 | ### set up 19 | 20 | ``` 21 | cp config/application-sample.yml config/application.yml 22 | cp config/async-sample.yml config/async.yml 23 | cp config/evernote-sample.yml config/evernote.yml 24 | cp config/github-sample.yml config/github.yml 25 | cp config/dropbox-sample.yml config/dropbox.yml 26 | cp config/facebook-sample.yml config/facebook.yml 27 | cp config/paperclip_feedbacks_s3-sample.yml config/paperclip_feedbacks_s3.yml 28 | # edit above yaml files 29 | 30 | bundle install --path vendor/bundle 31 | bundle exec rake sunspot:solr:run 32 | bundle exec rake db:migrate 33 | ln -s `pwd` ~/.pow/wripe 34 | ``` 35 | 36 | 37 | ### run 38 | 39 | You can login with GitHub or Facebook. 40 | 41 | #### except Chrome 42 | 43 | ``` 44 | bundle exec rake sunspot:solr:run # in case of stopping solr 45 | open "http://wripe.dev/" 46 | ``` 47 | 48 | #### with Chrome 49 | 50 | ``` 51 | bundle exec rake sunspot:solr:run # in case of stopping solr 52 | bundle exec rails s 53 | open "http://lvh.me:3000/" 54 | ``` 55 | 56 | 57 | ### test 58 | 59 | #### with PhantomJS 60 | 61 | ``` 62 | bundle exec rake 63 | ``` 64 | 65 | #### with Chrome 66 | 67 | ``` 68 | bundle exec rake DRIVER=chrome 69 | ``` 70 | 71 | #### with Safari 72 | 73 | ``` 74 | bundle exec rake DRIVER=safari 75 | ``` 76 | 77 | #### with Firefox 78 | 79 | ``` 80 | bundle exec rake DRIVER=firefox 81 | ``` 82 | 83 | #### with Internet Explorer 84 | 85 | windows側でselenium-serverとstone 10.0.0.3:57124 57124を起動 86 | 87 | ``` 88 | bundle exec rake DRIVER=ie 89 | ``` 90 | -------------------------------------------------------------------------------- /app/assets/stylesheets/toggle.css.scss: -------------------------------------------------------------------------------- 1 | //= require shared/toggle-switch 2 | 3 | .flat.toggle { 4 | background-color: #95a5a6; 5 | color: #fff; 6 | border-radius: 4px; 7 | -moz-border-radius: 4px; 8 | } 9 | 10 | .flat.switch { 11 | overflow: hidden; 12 | } 13 | 14 | .flat.switch .slide-button { 15 | background-color: #2980b9; 16 | 17 | -webkit-transform: skew(20deg) translateX(10px); 18 | -moz-transform: skew(20deg) translateX(10px); 19 | -ms-transform: skew(20deg) translateX(10px); 20 | transform: skew(20deg) translateX(10px); 21 | } 22 | 23 | .flat.toggle .slide-button { 24 | border-radius: 4px; 25 | background-color: #848484; 26 | } 27 | 28 | /* Selected ON toggle */ 29 | .flat.toggle input:first-of-type:checked ~ .slide-button { 30 | background-color: #2980b9; 31 | } 32 | 33 | .flat.switch input:first-of-type:checked ~ .slide-button { 34 | -webkit-transform: skew(20deg) translateX(-10px); 35 | -moz-transform: skew(20deg) translateX(-10px); 36 | -ms-transform: skew(20deg) translateX(-10px); 37 | -o-transform: skew(20deg) translateX(-10px); 38 | transform: skew(20deg) translateX(-10px); 39 | } 40 | 41 | .flat p { 42 | color: #333; 43 | } 44 | 45 | .flat span { 46 | color: #fff; 47 | } 48 | 49 | .flat.switch, 50 | .flat span { 51 | text-transform: uppercase; 52 | } 53 | 54 | /* Fix Android/Holo Theme in firefox - force absolute position */ 55 | .flat.switch input { 56 | top: 0; 57 | left: 0; 58 | } 59 | 60 | .flat:after { 61 | clear: left; 62 | } 63 | 64 | .flat select { 65 | -webkit-appearance: button; 66 | -moz-appearance: button; 67 | appearance: button; 68 | border-radius:6px; 69 | width: 200px; 70 | height: 32px; 71 | line-height: 32px; 72 | font-size: 13px; 73 | text-indent: 1em; 74 | color: #FFFFFF; 75 | border: none; 76 | cursor: pointer; 77 | background: #b6b6b6; 78 | background-size:40px 32px; 79 | background-position: right center; 80 | } -------------------------------------------------------------------------------- /app/controllers/calendar_controller.rb: -------------------------------------------------------------------------------- 1 | require 'icalendar' 2 | 3 | class CalendarController < ApplicationController 4 | include ActionView::Helpers::TextHelper 5 | before_filter :required_login, except: [:export] 6 | 7 | def export 8 | prop = UserProperty.where(key: 'export-key', value: params[:key]).first 9 | if prop 10 | @user = prop.user 11 | @pages = @user.pages.inbox.date_range(90.days.ago, 90.days.from_now) 12 | respond_to do |format| 13 | format.ics { 14 | calendar = Icalendar::Calendar.new 15 | calendar.prodid = 'wri.pe' 16 | calendar.custom_property("X-WR-CALNAME;VALUE=TEXT", "wri.pe - #{@user.username}") 17 | calendar.custom_property("X-WR-CALDESC;VALUE=TEXT", "https://wri.pe/") 18 | calendar.custom_property("X-PUBLISHED-TTL", "PT1H") 19 | @pages.each do |page| 20 | page.dates.each do |date| 21 | event = Icalendar::Event.new 22 | event.dtstart = date.date 23 | event.dtstart.ical_params = { "VALUE" => "DATE" } 24 | event.dtend = date.date 25 | event.dtend.ical_params = { "VALUE" => "DATE" } 26 | event.summary = "#{page.title} - wri.pe" 27 | event.description = truncate(page.body, :length => 100) 28 | event.created = page.created_at.utc.strftime("%Y%m%dT%H%M%SZ") 29 | event.last_modified = page.updated_at.utc.strftime("%Y%m%dT%H%M%SZ") 30 | event.uid = page.url 31 | event.url = page.edit_url 32 | calendar.add_event(event) 33 | end 34 | end 35 | calendar.publish 36 | render text: calendar.to_ical 37 | } 38 | end 39 | else 40 | head 404 41 | end 42 | end 43 | 44 | def generate_export_key 45 | current_user.generate_export_key 46 | render text: "{}", status: 201 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/features/search_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | def create_test_users 4 | user = FactoryGirl.create(:testdrive1) 5 | user.pages.create :title => 'TITLE1', :body => 'BODY1' 6 | user.pages.create :title => 'TITLE2', :body => 'BODY2' 7 | 8 | user = FactoryGirl.create(:testdrive2) 9 | user.pages.create :title => 'TITLE1 a', :body => 'BODY1 a' 10 | user.pages.create :title => 'TITLE2 a', :body => 'BODY2 a' 11 | end 12 | 13 | feature 'Search', :js => true, :solr => true do 14 | scenario 'search in title' do 15 | create_test_users 16 | test_login 'testdrive1' 17 | 18 | wait_and_find('#navigator-search').click 19 | 20 | wait_and_find('#list-page-search-query').set "TITLE1" 21 | wait_and_find('#list-page-search-submit').click 22 | 23 | wait_until { not find('#list-page').text.blank? } 24 | page.should have_content('TITLE1') 25 | page.should_not have_content('TITLE2') 26 | page.should_not have_content('TITLE1 a') 27 | page.should_not have_content('TITLE2 a') 28 | 29 | find('#list-page-search-query').set "TITLE2" 30 | find('#list-page-search-submit').click 31 | 32 | wait_until { not find('#list-page').text.blank? } 33 | page.should_not have_content('TITLE1') 34 | page.should have_content('TITLE2') 35 | page.should_not have_content('TITLE1 a') 36 | page.should_not have_content('TITLE2 a') 37 | end 38 | 39 | scenario 'search in body' do 40 | create_test_users 41 | test_login 'testdrive1' 42 | 43 | wait_and_find('#navigator-search').click 44 | 45 | wait_and_find('#list-page-search-query').set "BODY1" 46 | wait_and_find('#list-page-search-submit').click 47 | 48 | wait_until { not find('#list-page').text.blank? } 49 | page.should have_content('TITLE1') 50 | page.should_not have_content('TITLE2') 51 | page.should_not have_content('TITLE1 a') 52 | page.should_not have_content('TITLE2 a') 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/controllers/messages_controller.rb: -------------------------------------------------------------------------------- 1 | class MessagesController < ApplicationController 2 | before_filter :required_login 3 | PER_PAGE = 10 4 | 5 | def index 6 | load_messages current_user.messages 7 | render_messages \ 8 | old_messages_url: -> { "/messages.json?old=#{@messages.last.modified_at}" }, 9 | new_messages_url: -> { "/messages.json?new=#{@messages.first.modified_at}" } 10 | end 11 | 12 | 13 | private 14 | def load_messages(finder) 15 | @messages = finder.includes(:page, :user).limit(PER_PAGE) 16 | @total_messages = finder.count 17 | @current_index = false 18 | 19 | if params[:old] 20 | @messages = @messages.where('user_messages.created_at < ?', params[:old].to_i).order('user_messages.created_at DESC').load 21 | elsif params[:new] 22 | @messages = @messages.where('user_messages.created_at > ?', params[:new].to_i).order('user_messages.created_at ASC').load.sort do |a, b| 23 | b.modified_at <=> a.modified_at 24 | end 25 | else 26 | @messages = @messages.order('user_messages.created_at DESC').load 27 | @current_index = 0 28 | end 29 | @current_index ||= finder.where('user_messages.created_at > ?', @messages.first.modified_at).order('user_messages.created_at DESC').count 30 | end 31 | 32 | private 33 | def render_messages(old_messages_url: nil, new_messages_url: nil, messages: nil, options: {}) 34 | messages ||= @messages.map { |p| p.to_hash(user: current_user, body: false) } 35 | @current_index ||= 0 36 | respond_to do |format| 37 | format.json do 38 | render json: { 39 | index: @current_index, 40 | total_messages: @total_messages, 41 | old_messages_url: @current_index + @messages.count < @total_messages ? old_messages_url.call : nil, 42 | new_messages_url: @current_index > 0 ? new_messages_url.call : nil, 43 | messages: messages.map(&:to_hash) 44 | }.merge(options) 45 | end 46 | end 47 | end 48 | 49 | end 50 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/html2canvas/Font.js: -------------------------------------------------------------------------------- 1 | _html2canvas.Util.Font = (function () { 2 | 3 | var fontData = {}; 4 | 5 | return function(font, fontSize, doc) { 6 | if (fontData[font + "-" + fontSize] !== undefined) { 7 | return fontData[font + "-" + fontSize]; 8 | } 9 | 10 | var container = doc.createElement('div'), 11 | img = doc.createElement('img'), 12 | span = doc.createElement('span'), 13 | sampleText = 'Hidden Text', 14 | baseline, 15 | middle, 16 | metricsObj; 17 | 18 | container.style.visibility = "hidden"; 19 | container.style.fontFamily = font; 20 | container.style.fontSize = fontSize; 21 | container.style.margin = 0; 22 | container.style.padding = 0; 23 | 24 | doc.body.appendChild(container); 25 | 26 | // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) 27 | img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs="; 28 | img.width = 1; 29 | img.height = 1; 30 | 31 | img.style.margin = 0; 32 | img.style.padding = 0; 33 | img.style.verticalAlign = "baseline"; 34 | 35 | span.style.fontFamily = font; 36 | span.style.fontSize = fontSize; 37 | span.style.margin = 0; 38 | span.style.padding = 0; 39 | 40 | span.appendChild(doc.createTextNode(sampleText)); 41 | container.appendChild(span); 42 | container.appendChild(img); 43 | baseline = (img.offsetTop - span.offsetTop) + 1; 44 | 45 | container.removeChild(span); 46 | container.appendChild(doc.createTextNode(sampleText)); 47 | 48 | container.style.lineHeight = "normal"; 49 | img.style.verticalAlign = "super"; 50 | 51 | middle = (img.offsetTop-container.offsetTop) + 1; 52 | metricsObj = { 53 | baseline: baseline, 54 | lineWidth: 1, 55 | middle: middle 56 | }; 57 | 58 | fontData[font + "-" + fontSize] = metricsObj; 59 | 60 | doc.body.removeChild(container); 61 | 62 | return metricsObj; 63 | }; 64 | })(); 65 | -------------------------------------------------------------------------------- /app/views/shared/_settings.html.erb: -------------------------------------------------------------------------------- 1 | <%= render_dialog 'settings-backup', 'Daily backup', cancel: 'Close' do %> 2 |
3 |
4 |

5 | Daily automatic backup to Dropbox.
6 | It stores up until 14 generations of changes.
7 | Your files are private and can't be accessed by our service. 8 |

9 |

10 | Current status: Off
Use dropbox 11 |

12 |

13 | Processing Dropbox connection in other window 14 |

15 |

16 | Current status: On
Turn off automatic backup 17 |

18 |
19 | <% end %> 20 | 21 | <%= render_dialog 'settings-export', 'Download all notes', cancel: 'Close', 22 | action: { :class => 'btn-primary', :action => 'download', :label => 'Download zipped notes' } do %> 23 |
24 |

25 | You can download all your notes. 26 |

27 |

28 | We recommend to use what makes automatical daily backup into your Droxbox. 29 |

30 |
31 | <% end %> 32 | 33 | <%= render_dialog 'settings-evernote', 'Connect with Evernote', cancel: 'Close' do %> 34 |
35 |
36 |

37 | Realtime automatic sync to Evernote.
38 |

39 |

40 | Current status: Off
Connect with Evernote 41 |

42 |

43 | Processing Evernote connection in other window 44 |

45 |

46 | Current status: Already connected
47 | Turn off Evernote sync 48 |

49 |
50 | <% end %> 51 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/html2canvas/Support.js: -------------------------------------------------------------------------------- 1 | _html2canvas.Util.Support = function (options, doc) { 2 | 3 | function supportSVGRendering() { 4 | var img = new Image(), 5 | canvas = doc.createElement("canvas"), 6 | ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); 7 | if (ctx === false) { 8 | return false; 9 | } 10 | canvas.width = canvas.height = 10; 11 | img.src = [ 12 | "data:image/svg+xml,", 13 | "", 14 | "", 15 | "
", 16 | "sup", 17 | "
", 18 | "
", 19 | "
" 20 | ].join(""); 21 | try { 22 | ctx.drawImage(img, 0, 0); 23 | canvas.toDataURL(); 24 | } catch(e) { 25 | return false; 26 | } 27 | h2clog('html2canvas: Parse: SVG powered rendering available'); 28 | return true; 29 | } 30 | 31 | // Test whether we can use ranges to measure bounding boxes 32 | // Opera doesn't provide valid bounds.height/bottom even though it supports the method. 33 | 34 | function supportRangeBounds() { 35 | var r, testElement, rangeBounds, rangeHeight, support = false; 36 | 37 | if (doc.createRange) { 38 | r = doc.createRange(); 39 | if (r.getBoundingClientRect) { 40 | testElement = doc.createElement('boundtest'); 41 | testElement.style.height = "123px"; 42 | testElement.style.display = "block"; 43 | doc.body.appendChild(testElement); 44 | 45 | r.selectNode(testElement); 46 | rangeBounds = r.getBoundingClientRect(); 47 | rangeHeight = rangeBounds.height; 48 | 49 | if (rangeHeight === 123) { 50 | support = true; 51 | } 52 | doc.body.removeChild(testElement); 53 | } 54 | } 55 | 56 | return support; 57 | } 58 | 59 | return { 60 | rangeBounds: supportRangeBounds(), 61 | svgRendering: options.svgRendering && supportSVGRendering() 62 | }; 63 | }; -------------------------------------------------------------------------------- /lib/ar_async.rb: -------------------------------------------------------------------------------- 1 | require 'base64' 2 | require 'aws-sdk' 3 | 4 | module ActiveRecordAsync 5 | def self.config 6 | @config ||= YAML.load(open(File.join(Rails.root, 'config', 'async.yml')))[Rails.env] 7 | end 8 | 9 | def self.sqs 10 | @sqs ||= AWS::SQS::Queue.new(ActiveRecordAsync.config['sqs']) 11 | end 12 | 13 | def async(methodname, args=[]) 14 | message = async_message_dump(methodname, args) 15 | if ActiveRecordAsync.config['inline'] 16 | ActiveRecordAsync.async_message_run(message) 17 | else 18 | if ActiveRecordAsync.async_batch_messages.nil? 19 | ActiveRecordAsync.sqs.send_message(message) 20 | else 21 | ActiveRecordAsync.async_batch_messages << message 22 | end 23 | end 24 | end 25 | 26 | def self.async_batch_messages 27 | @_async_batch_messages 28 | end 29 | 30 | def self.async_batch 31 | @_async_batch_messages = [] if @_async_batch_messages.nil? 32 | yield 33 | @_async_batch_messages.each_slice(10) do |messages| 34 | self.sqs.batch_send(*messages) 35 | end 36 | ensure 37 | @_async_batch_messages = [] 38 | end 39 | 40 | def self.runner 41 | unless config['inline'] 42 | sqs.poll do |data| 43 | begin 44 | message = async_message_run(data.body) 45 | puts "Got message: #{message[:method]}" 46 | rescue Exception => e 47 | p e 48 | end 49 | end 50 | end 51 | end 52 | 53 | private 54 | def async_message_dump(methodname, args) 55 | Base64.encode64(Marshal.dump({method: methodname, obj: self, args:args})) 56 | end 57 | 58 | private 59 | def self.async_message_run(data) 60 | message = async_message_load(data) 61 | message[:result] = message[:obj].send message[:method], *message[:args] 62 | message 63 | end 64 | 65 | private 66 | def self.async_message_load(data) 67 | Marshal.load(Base64.decode64(data)) 68 | end 69 | end 70 | 71 | AWS.config( 72 | access_key_id: ActiveRecordAsync.config['access_key_id'], 73 | secret_access_key: ActiveRecordAsync.config['secret_access_key'] 74 | ) 75 | 76 | 77 | ActiveRecord::Base.send :include, ActiveRecordAsync 78 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/analytics.js.coffee: -------------------------------------------------------------------------------- 1 | class Analytics 2 | collect_url: "https://ssl.google-analytics.com/collect" 3 | 4 | constructor: (@tracking_id) -> 5 | if @tracking_id 6 | storage = window.localStorage || {} 7 | storage.analytics_cid = parseInt((new Date()).getTime()*Math.random()) unless storage.analytics_cid 8 | @cid = parseInt(storage.analytics_cid) 9 | 10 | pageview: (path) -> 11 | return unless @tracking_id 12 | return unless navigator.onLine 13 | data = 14 | v: 1 # Version 15 | t: 'pageview' # Hit type 16 | tid: @tracking_id 17 | cid: @cid # anonymous customer ID 18 | dp: path 19 | 20 | $.ajax 21 | url: @collect_url 22 | method: 'POST' 23 | data: data 24 | 25 | event: (options) -> 26 | return unless @tracking_id 27 | return unless navigator.onLine 28 | data = 29 | v: 1 # Version 30 | t: 'event' # Hit type 31 | tid: @tracking_id 32 | cid: @cid # anonymous customer ID 33 | for value, key of options 34 | data[key] = value 35 | 36 | $.ajax 37 | url: @collect_url 38 | method: 'POST' 39 | data: data 40 | 41 | ### 42 | https://ssl.google-analytics.com/collect 43 | ?v=1 44 | &t=event // Hit type 45 | &tid=UA-7634164-5 // my profil ID 46 | &cid=555 // anonymous customer ID 47 | &dh=myofflinestore.com // my "hostname" =) 48 | &ec=Motion%20Detector // Event category 49 | &ea=In // Customer direction: going in or out? 50 | &ev=1 // Event value 51 | &cm5=1 // Custom metric (+1 increment) 52 | 53 | v=1 // Version. 54 | &tid=UA-XXXX-Y // Tracking ID / Web property / Property ID. 55 | &cid=555 // Anonymous Client ID. 56 | 57 | &t=event // Event hit type 58 | &ec=video // Event Category. Required. 59 | &ea=play // Event Action. Required. 60 | &el=holiday // Event label. 61 | &ev=300 // Event value. 62 | 63 | https://ssl.google-analytics.com/collect?v=1&t=event&tid=UA-40504922-4&cid=555&dh=wri.pe&ec=test 64 | ### 65 | 66 | if location.host == 'wri.pe' 67 | window.analytics = new Analytics("UA-40504922-4") 68 | else 69 | window.analytics = new Analytics() 70 | -------------------------------------------------------------------------------- /app/assets/javascripts/app/markdown_toolbar.js.coffee: -------------------------------------------------------------------------------- 1 | #= require shared/utils 2 | #= require shared/modal_dialog 3 | 4 | class InsetLinkDialog extends ModalDialog 5 | el: $('#edit-page-insert-link') 6 | title_el: $('#edit-page-insert-link-text') 7 | url_el: $('#edit-page-insert-link-url') 8 | focus_el: $('#edit-page-body') 9 | 10 | shown: -> 11 | delay 500, => @title_el.focus() 12 | 13 | action: (action) -> 14 | if action == 'link' 15 | markdownToolbar.insertTemplate("[#{@title_el.val()}](#{@url_el.val()})") 16 | delay 300, => @focus_el.focus() if @focus_el 17 | 18 | 19 | insertTextAtPosision = (obj, pos, txt, start_idx, end_idx) -> 20 | obj.focus() 21 | if (document.uniqueID) # if IE 22 | pos.text = txt; 23 | pos.select() 24 | else 25 | pos = 0 if pos < 0 26 | s = obj.value 27 | np = pos + txt.length 28 | obj.value = s.substr(0, pos) + txt + s.substr(pos) 29 | if typeof start_idx == 'undefined' 30 | start_idx = 0 31 | if typeof end_idx == 'undefined' 32 | end_idx = 0; 33 | obj.setSelectionRange(start_idx + np, end_idx + np) 34 | 35 | 36 | getCaretPosition = (obj) -> 37 | obj.focus() 38 | if document.uniqueID then document.selection.createRange() else obj.selectionStart 39 | 40 | 41 | class MarkdownToolbar 42 | el: $('#edit-page-body') 43 | 44 | insertLink: -> 45 | dialog = new InsetLinkDialog() 46 | dialog.show() 47 | 48 | insertTemplate: (text, start_idx, end_idx) -> 49 | head = getCaretPosition(@el[0]) 50 | 51 | if text.substring(0,1) == "\n" 52 | text = text.substring(1) 53 | body = @el.val() 54 | if body.substring(head, head+1) == "\n" 55 | --head; 56 | while head > 0 57 | if body.substring(head, head + 1) == "\n" 58 | insertTextAtPosision(@el[0], head + 1, text, start_idx, end_idx) 59 | return 60 | --head 61 | insertTextAtPosision(@el[0], head, text, start_idx, end_idx) 62 | 63 | insertToday: () -> 64 | insertTextAtPosision(@el[0], getCaretPosition(@el[0]), today_string()) 65 | 66 | insertTab: () -> 67 | insertTextAtPosision(@el[0], getCaretPosition(@el[0]), "\t") 68 | 69 | window.markdownToolbar = new MarkdownToolbar() 70 | -------------------------------------------------------------------------------- /app/views/app/_edit_help.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Hot keys

3 |

4 |

9 |

10 | 11 |

12 | You can use [Alt] and ⌘ instead of [Ctrl]. 13 |

14 | 15 |

Calendar

16 |

17 | Dates written in the title or body of your notes are automatically inserted in your calendar.
18 | ex) 2013/1/1, 2013-2-1, March 1, 2013 19 |

20 | 21 |

Actions

22 |

23 | If you want to delete this note, open " Setting" tab. 24 |

25 | 26 |

Markdown reference

27 | 28 |
Format
29 |
30 |
Italic
31 |
*example* or _example_
32 | 33 |
Bold
34 |
**example** or __example__
35 |
36 | 37 |
Links
38 |
39 |
link
40 |
URL
41 | 42 |
with text
43 |
[text](URL)
44 |
45 | 46 |
Headings
47 |
48 |
first level
49 |
# title
50 | 51 |
second level
52 |
## title
53 | 54 |
third level
55 |
### title
56 |
57 | 58 |
Lists
59 |
60 |
first level
61 |
- item
62 | 63 |
second level
64 |
 - item
65 | 66 |
Ordered Lists
67 |
1.  item
68 |
69 | 70 |
Block Quotes
71 |
72 |
first level
73 |
> block
74 | 75 |
second level
76 |
>> block
77 | 78 |
Code block
79 |
```
Your code
```
80 |
81 | 82 |
More
83 |
84 |
code
85 |
`code`
86 | 87 |
Horizontal Rule
88 |
---
89 |
90 |
91 | -------------------------------------------------------------------------------- /app/views/pages/show.html.erb: -------------------------------------------------------------------------------- 1 | 32 | 33 | <%= render_top_nav do %> 34 |
  • <%= @page.title %>
  • 35 | <% end %> 36 | 37 |
    38 |
    39 |
    40 |
    41 |
      42 |
    43 |
    44 |
    45 |
    46 |
    47 |
      48 |
    49 |
    50 |
    51 |
    52 |
    53 | 54 | <%= javascript_include_tag :app, :defer =>"defer" %> 55 | -------------------------------------------------------------------------------- /spec/features/signin_evernote_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | def signout_evernote 4 | visit 'https://sandbox.evernote.com/Logout.action' 5 | end 6 | 7 | feature 'Evernote', :js => true do 8 | =begin 9 | # disabled evernote signin feature 10 | scenario 'signin' do 11 | if %i(iphone ipad safari firefox selenium webkit).include?(Capybara.javascript_driver) 12 | pending "#{Capybara.javascript_driver} doesn't support this test" 13 | else 14 | signout_evernote 15 | 16 | clear_session 17 | visit '/' 18 | evaluate_script("$('.secret').css('display', 'inline')") 19 | find('#signin-evernote').click 20 | 21 | wait_until(10) do 22 | page.has_css?('#username') 23 | end 24 | 25 | test_user = EvernoteAuth.config['tests'].first 26 | find('#username').set test_user['email'] 27 | find('#password').set test_user['password'] 28 | find('#login').click 29 | 30 | wait_and_find_xpath("//input[@name='reauthorize' or @name='authorize']").click 31 | 32 | evaluate_script('session.username()').should == test_user['account'] 33 | 34 | current_path.should == '/app' 35 | user = User.find_by_username test_user['account'] 36 | user.pages.should be_empty 37 | end 38 | end 39 | =end 40 | 41 | scenario 'connect with existing account' do 42 | if %i(iphone ipad ie safari firefox selenium webkit).include?(Capybara.javascript_driver) 43 | pending "#{Capybara.javascript_driver} doesn't support this test" 44 | else 45 | signout_evernote 46 | 47 | user = FactoryGirl.create(:testdrive1) 48 | test_login 'testdrive1' 49 | find('#nav-username-link').click 50 | 51 | wait_and_find_css('#settings-evernote-button').click 52 | sleep 1.0 53 | wait_and_find_css('#settings-evernote-turnon-button').click 54 | 55 | within_window 'wripe_auth' do 56 | test_user = EvernoteAuth.config['tests'].first 57 | wait_and_find_css('#username').set test_user['email'] 58 | find('#password').set test_user['password'] 59 | find('#login').click 60 | 61 | wait_and_find_xpath("//input[@name='reauthorize' or @name='authorize']").click 62 | end 63 | 64 | wait_until_visible('#settings-evernote-turnedon') 65 | 66 | current_path.should == '/app' 67 | user.pages.should be_empty 68 | end 69 | end 70 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # recommendation!! 4 | #ruby '2.0.0' 5 | 6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 7 | gem 'rails', '4.0.3' 8 | gem "guard-coffeescript", "~> 1.2.1" 9 | 10 | gem 'koala' 11 | gem 'octokit' 12 | gem 'oauth2' 13 | 14 | gem 'execjs' 15 | gem 'therubyracer' 16 | gem 'yajl-ruby' 17 | gem 'json', '>= 1.8.1' 18 | 19 | group :development, :test do 20 | gem 'sqlite3' 21 | gem 'rspec-rails' 22 | gem 'capybara' 23 | gem 'capybara-webkit' 24 | gem 'selenium-webdriver' 25 | gem 'factory_girl' 26 | gem 'database_rewinder' 27 | gem 'sunspot-rails-tester' 28 | gem 'ruby-prof' 29 | end 30 | 31 | group :production do 32 | gem 'pg' 33 | gem 'newrelic_rpm' 34 | gem 'rails_12factor' 35 | end 36 | 37 | # server 38 | gem 'foreman' 39 | gem 'unicorn' 40 | 41 | # gem 'mysql2' 42 | 43 | # memcached 44 | gem 'dalli' 45 | 46 | # attach file 47 | gem "paperclip", "~> 3.0" 48 | gem 'aws-sdk', '~> 1.11.0' 49 | 50 | # markdown 51 | gem "redcarpet", "~> 2.3.0" 52 | gem "nokogiri", "~> 1.6.0" 53 | gem "sanitize", "~> 2.0.4" 54 | 55 | # paginator 56 | gem 'kaminari' 57 | 58 | # fulltext search 59 | gem 'sunspot_rails' 60 | gem 'sunspot_solr' 61 | 62 | # backup to dropbox 63 | gem 'dropbox-api' 64 | gem "rubyzip", "~> 0.9.9" 65 | 66 | # evernote 67 | gem "evernote_oauth", "~> 0.2.1" 68 | 69 | # ics export 70 | gem 'icalendar' 71 | 72 | # create HTML5 cache manifest 73 | gem 'rack-offline', :git => 'https://github.com/wycats/rack-offline.git' 74 | 75 | # Gems used only for assets and not required 76 | # in production environments by default. 77 | group :assets do 78 | gem 'sass-rails', '~> 4.0.0' 79 | gem 'coffee-rails', '~> 4.0.0' 80 | 81 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes 82 | # gem 'therubyracer', platforms: :ruby 83 | 84 | gem 'uglifier', '>= 1.0.3' 85 | end 86 | 87 | gem 'jquery-rails' 88 | gem 'less-rails' 89 | 90 | gem 'settingslogic' 91 | 92 | # To use ActiveModel has_secure_password 93 | # gem 'bcrypt-ruby', '~> 3.0.0' 94 | 95 | # To use Jbuilder templates for JSON 96 | # gem 'jbuilder' 97 | 98 | # Use unicorn as the app server 99 | # gem 'unicorn' 100 | 101 | # Deploy with Capistrano 102 | # gem 'capistrano' 103 | 104 | # To use debugger 105 | # gem 'debugger' 106 | -------------------------------------------------------------------------------- /app/views/evernote_auth/callback.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Evernote authentication - wri.pe 8 | 74 | 75 | <% if @error == :already_connected %> 76 |
    77 |

    Connect with Evernote

    78 | 79 |
    80 |
    81 |

    82 | This Evernote account already connected with other wri.pe account.
    83 |

    84 |
    85 | Cancel to connect with Evernote 86 |
    87 | <% else %> 88 | Close this window 89 | 97 | <% end %> 98 | 99 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/jquery.bootstrap-growl.js: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/ifightcrime/bootstrap-growl 3 | 4 | The MIT License 5 | 6 | Copyright (c) Nick Larson, http://github.com/ifightcrime 7 | */ 8 | (function() { 9 | var $; 10 | 11 | $ = jQuery; 12 | 13 | $.bootstrapGrowl = function(message, options) { 14 | var $alert, css, offsetAmount; 15 | 16 | options = $.extend({}, $.bootstrapGrowl.default_options, options); 17 | $alert = $("
    "); 18 | $alert.attr("class", "bootstrap-growl alert"); 19 | if (options.type) { 20 | $alert.addClass("alert-" + options.type); 21 | } 22 | if (options.allow_dismiss) { 23 | $alert.append("×"); 24 | } 25 | $alert.append(message); 26 | if (options.top_offset) { 27 | options.offset = { 28 | from: "top", 29 | amount: options.top_offset 30 | }; 31 | } 32 | offsetAmount = options.offset.amount; 33 | $(".bootstrap-growl").each(function() { 34 | return offsetAmount = Math.max(offsetAmount, parseInt($(this).css(options.offset.from)) + $(this).outerHeight() + options.stackup_spacing); 35 | }); 36 | css = { 37 | "position": (options.ele === "body" ? "fixed" : "absolute"), 38 | "margin": 0, 39 | "z-index": "9999", 40 | "display": "none" 41 | }; 42 | css[options.offset.from] = offsetAmount + "px"; 43 | $alert.css(css); 44 | if (options.width !== "auto") { 45 | $alert.css("width", options.width + "px"); 46 | } 47 | $(options.ele).append($alert); 48 | switch (options.align) { 49 | case "center": 50 | $alert.css({ 51 | "left": "50%", 52 | "margin-left": "-" + ($alert.outerWidth() / 2) + "px" 53 | }); 54 | break; 55 | case "left": 56 | $alert.css("left", "20px"); 57 | break; 58 | default: 59 | $alert.css("right", "20px"); 60 | } 61 | $alert.fadeIn(); 62 | if (options.delay > 0) { 63 | return $alert.delay(options.delay).fadeOut(function() { 64 | return $(this).remove(); 65 | }); 66 | } 67 | }; 68 | 69 | $.bootstrapGrowl.default_options = { 70 | ele: "body", 71 | type: null, 72 | offset: { 73 | from: "top", 74 | amount: 20 75 | }, 76 | align: "right", 77 | width: 250, 78 | delay: 4000, 79 | allow_dismiss: true, 80 | stackup_spacing: 10 81 | }; 82 | 83 | }).call(this); 84 | 85 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/html2canvas/Util.js: -------------------------------------------------------------------------------- 1 | window.html2canvas = function(elements, opts) { 2 | elements = (elements.length) ? elements : [elements]; 3 | var queue, 4 | canvas, 5 | options = { 6 | // general 7 | logging: false, 8 | elements: elements, 9 | background: "#fff", 10 | 11 | // preload options 12 | proxy: null, 13 | timeout: 0, // no timeout 14 | useCORS: false, // try to load images as CORS (where available), before falling back to proxy 15 | allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true 16 | 17 | // parse options 18 | svgRendering: false, // use svg powered rendering where available (FF11+) 19 | ignoreElements: "IFRAME|OBJECT|PARAM", 20 | useOverflow: true, 21 | letterRendering: false, 22 | chinese: false, 23 | 24 | // render options 25 | 26 | width: null, 27 | height: null, 28 | taintTest: true, // do a taint test with all images before applying to canvas 29 | renderer: "Canvas" 30 | }; 31 | 32 | options = _html2canvas.Util.Extend(opts, options); 33 | 34 | _html2canvas.logging = options.logging; 35 | options.complete = function( images ) { 36 | 37 | if (typeof options.onpreloaded === "function") { 38 | if ( options.onpreloaded( images ) === false ) { 39 | return; 40 | } 41 | } 42 | queue = _html2canvas.Parse( images, options ); 43 | 44 | if (typeof options.onparsed === "function") { 45 | if ( options.onparsed( queue ) === false ) { 46 | return; 47 | } 48 | } 49 | 50 | canvas = _html2canvas.Renderer( queue, options ); 51 | 52 | if (typeof options.onrendered === "function") { 53 | options.onrendered( canvas ); 54 | } 55 | 56 | 57 | }; 58 | 59 | // for pages without images, we still want this to be async, i.e. return methods before executing 60 | window.setTimeout( function(){ 61 | _html2canvas.Preload( options ); 62 | }, 0 ); 63 | 64 | return { 65 | render: function( queue, opts ) { 66 | return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) ); 67 | }, 68 | parse: function( images, opts ) { 69 | return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) ); 70 | }, 71 | preload: function( opts ) { 72 | return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) ); 73 | }, 74 | log: h2clog 75 | }; 76 | }; 77 | 78 | window.html2canvas.log = h2clog; // for renderers 79 | window.html2canvas.Renderer = { 80 | Canvas: undefined // We are assuming this will be used 81 | }; -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/jquery.hotkeys.js: -------------------------------------------------------------------------------- 1 | // keymaster.js 2 | // (c) 2011-2012 Thomas Fuchs 3 | // keymaster.js may be freely distributed under the MIT license. 4 | (function(e){function a(e,t){var n=e.length;while(n--)if(e[n]===t)return n;return-1}function f(e,t){var i,o,f,l,c;i=e.keyCode,a(u,i)==-1&&u.push(i);if(i==93||i==224)i=91;if(i in r){r[i]=!0;for(f in s)s[f]==i&&(h[f]=!0);return}if(!h.filter.call(this,e))return;if(!(i in n))return;for(l=0;l0;for(f in r)if(!r[f]&&a(o.mods,+f)>-1||r[f]&&a(o.mods,+f)==-1)c=!1;(o.mods.length==0&&!r[16]&&!r[18]&&!r[17]&&!r[91]||c)&&o.method(e,o)===!1&&(e.preventDefault?e.preventDefault():e.returnValue=!1,e.stopPropagation&&e.stopPropagation(),e.cancelBubble&&(e.cancelBubble=!0))}}}function l(e){var t=e.keyCode,n,i=a(u,t);i>=0&&u.splice(i,1);if(t==93||t==224)t=91;if(t in r){r[t]=!1;for(n in s)s[n]==t&&(h[n]=!1)}}function c(){for(t in r)r[t]=!1;for(t in s)h[t]=!1}function h(e,t,r){var i,u,a,f;r===undefined&&(r=t,t="all"),e=e.replace(/\s/g,""),i=e.split(","),i[i.length-1]==""&&(i[i.length-2]+=",");for(a=0;a1){u=e.slice(0,e.length-1);for(f=0;f true do 63 | Sunspot::Rails::Tester.start_original_sunspot_session 64 | Sunspot.session = $original_sunspot_session 65 | Sunspot.remove_all! 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Wripe::Application.routes.draw do 2 | if Rails.env.production? 3 | offline = Rack::Offline.configure :cache_interval => 120 do 4 | %w(app).each do |c| 5 | cache ActionController::Base.helpers.asset_path("#{c}.css") 6 | cache ActionController::Base.helpers.asset_path("#{c}.js") 7 | end 8 | 9 | public_path = Rails.public_path 10 | Dir[public_path.join("fonts/*")].each do |file| 11 | cache '/'+Pathname.new(file).relative_path_from(public_path).to_s 12 | end 13 | 14 | Dir[public_path.join("images/*") ].flatten.each do |file| 15 | cache '/'+Pathname.new(file).relative_path_from(public_path).to_s 16 | end 17 | 18 | # cache other assets 19 | network "*" 20 | end 21 | get "/application.manifest" => offline 22 | end 23 | 24 | resources :pages, :constraints => { :id => /\d{1}\w{5,32}/ }, :only => [:new, :create, :index] do 25 | collection do 26 | get :archived 27 | get :calendar 28 | get :search 29 | get :tagged 30 | get :tags 31 | end 32 | resources :members, :controller => 'pages/members', :constraints => { :id => /\d+/ } 33 | end 34 | resources :pages, :constraints => { :id => /\d{1}\w{5,32}/ }, :except => [:new, :create, :index], :path => '/' do 35 | member do 36 | post :archive 37 | post :unarchive 38 | end 39 | end 40 | 41 | get 'calendar/exports/:key', :constraints => { :key => /\w+/ }, :controller => 'calendar', :action => 'export' 42 | post 'calendar/generate_export_key', :controller => 'calendar', :action => 'generate_export_key' 43 | 44 | resources :messages 45 | resources :helps, :constraints => { :id => /\w+/ }, :only => [:destroy] do 46 | collection do 47 | post :reset 48 | end 49 | end 50 | 51 | get 'app' => 'app#index', :as => 'home' 52 | 53 | get 'sign_out' => 'sessions#destroy', :as => 'sign_out' 54 | get 'session(.:format)' => 'sessions#show' 55 | if %w(test development).include?(Rails.env) 56 | get 'sessions/test' => 'sessions#test' 57 | end 58 | 59 | resource :settings 60 | 61 | namespace :fbauth do 62 | get 'sign_in' 63 | get 'callback' 64 | end 65 | 66 | namespace :ghauth do 67 | get 'sign_in' 68 | get 'callback' 69 | end 70 | 71 | namespace :dropbox_auth do 72 | get 'sign_in' 73 | get 'callback' 74 | end 75 | 76 | namespace :evernote_auth do 77 | get 'sign_in' 78 | get 'connect' 79 | get 'callback' 80 | end 81 | 82 | get 'export/:action', :controller => 'exports' 83 | 84 | resources :feedbacks 85 | 86 | get 'stats(.:format)' => 'stats#index' 87 | 88 | end 89 | -------------------------------------------------------------------------------- /app/assets/javascripts/models/page_collection.js.coffee: -------------------------------------------------------------------------------- 1 | //= require shared/underscore 2 | //= require shared/backbone 3 | //= require shared/defer 4 | //= require shared/localstorage 5 | //= require shared/jscache 6 | //= require session 7 | 8 | cache = new Cache(128 * 1024, false, new Cache.LocalStorageCacheStorage('page-collection')) 9 | 10 | class PageCollection extends Backbone.Events 11 | constructor: (url) -> 12 | _.extend @, Backbone.Events 13 | @url = url 14 | @clear() 15 | 16 | load: (use_cache)-> 17 | if use_cache 18 | cached_data = cache.getItem(@url) 19 | if cached_data 20 | try 21 | @_parse_data(cached_data) 22 | @load_from_network().done (data) => 23 | @trigger('update') 24 | defer = $.Deferred() 25 | defer.resolve() 26 | defer.promise() 27 | catch err 28 | cache.removeItem(@url) 29 | @load_from_network() 30 | else 31 | @load_from_network() 32 | else 33 | @load_from_network() 34 | 35 | load_from_network: -> 36 | Deferred (defer) => 37 | @request.abort() if @request 38 | @request = authorizedRequest(url: @url, type: 'GET') 39 | @request.done (data) => 40 | @_parse_data(data) 41 | cache.setItem(@url, data) 42 | @request = undefined 43 | defer.resolve() 44 | @request.fail (xhr, textStatus, errorThrows) => 45 | if !xhr.getAllResponseHeaders() 46 | defer.reject('aborted') 47 | else 48 | cache.removeItem(@url) 49 | defer.reject('error', textStatus, errorThrows) 50 | @request = undefined 51 | 52 | _parse_data: (data) -> 53 | @data = data 54 | @pages = data.pages.map (page_data) -> new Page(page_data); 55 | @index = data.index 56 | @total_pages = data.total_pages 57 | @old_pages_url = data.old_pages_url 58 | @new_pages_url = data.new_pages_url 59 | 60 | abort: -> 61 | @request.abort() if @request 62 | @request = undefined 63 | 64 | append: (collection) -> 65 | if collection 66 | @pages = @pages.concat collection.pages 67 | @total_pages = collection.total_pages 68 | @old_pages_url = collection.old_pages_url 69 | 70 | old_collection: -> 71 | if @old_pages_url 72 | new PageCollection(@old_pages_url) 73 | else 74 | undefined 75 | 76 | new_collection: -> 77 | if @new_pages_url 78 | new PageCollection(@new_pages_url) 79 | else 80 | undefined 81 | 82 | clear: -> 83 | @pages = [] 84 | @data = undefined 85 | @index = 0 86 | @total_pages = 0 87 | @old_page_url = undefined 88 | @new_page_url = undefined 89 | 90 | 91 | window.PageCollection = PageCollection 92 | -------------------------------------------------------------------------------- /lib/backup_dropbox.rb: -------------------------------------------------------------------------------- 1 | # 2 | require 'dropbox-api' 3 | require 'zip/zip' 4 | require './lib/backup_pages' 5 | 6 | 7 | dbconfig = YAML.load(open(File.join(Rails.root, 'config', 'dropbox.yml')))[Rails.env] 8 | 9 | Dropbox::API::Config.app_key = dbconfig['app_key'] 10 | Dropbox::API::Config.app_secret = dbconfig['app_secret'] 11 | Dropbox::API::Config.mode = dbconfig['mode'] 12 | 13 | 14 | class DropboxBackup 15 | BACKUP_AGE = 14 16 | 17 | def self.backups 18 | DropboxUser.includes(:user).each do |dbuser| 19 | user = dbuser.user 20 | if user.pages.where('pages.updated_at > ?', 1.days.ago).count > 0 21 | if backup(user) 22 | puts "OK: #{user.id}" 23 | else 24 | puts "NG: #{user.id}" 25 | end 26 | end 27 | end 28 | end 29 | 30 | def self.backup(user) 31 | return false unless user.dropbox_user 32 | 33 | zipfile = BackupPages.zip(user) 34 | client = Dropbox::API::Client.new :token => user.dropbox_user.auth_token, :secret => user.dropbox_user.auth_secret 35 | begin 36 | client.mkdir 'backups' 37 | rescue Dropbox::API::Error::Forbidden 38 | # no op 39 | end 40 | 41 | filename = "backups/wripe.backup.#{Time.now.strftime('%Y-%m-%d')}.zip" 42 | client.upload filename, open(zipfile).read 43 | 44 | backups = JSON.parse(user.dropbox_user.backups || '[]').push(filename) 45 | if backups.length > BACKUP_AGE 46 | file = client.file(backups.unshift) 47 | file.destroy if file 48 | user.dropbox_user.update_attribute :backups, JSON.generate(backups) 49 | end 50 | true 51 | rescue Exception => e 52 | puts e 53 | puts e.backtrace.join("\n") 54 | false 55 | ensure 56 | FileUtils.rm_rf zipfile if zipfile && File.exists?(zipfile) 57 | end 58 | 59 | def consumer 60 | @consumer ||= Dropbox::API::OAuth.consumer(:authorize) 61 | end 62 | 63 | def request_auth(callback) 64 | request_token = consumer.get_request_token 65 | return request_token.authorize_url(oauth_callback: callback), { 66 | oauth_token: request_token.token, 67 | oauth_token_secret: request_token.secret 68 | } 69 | end 70 | 71 | def authorize(oauth_token, hash) 72 | request_token = OAuth::RequestToken.from_hash(consumer, hash) 73 | request_token.get_access_token(:oauth_verifier => oauth_token) 74 | end 75 | 76 | def remove_old_backup 77 | client = Dropbox::API::Client.new :token => user.dropbox_user.auth_token, :secret => user.dropbox_user.auth_secret 78 | file = client.file("backups/wripe.backup.#{BACKUP_AGE.days.ago.to_time.strftime('%Y-%m-%d')}.zip") 79 | file.destroy if file 80 | 81 | file = client.file("wripe.backup.#{BACKUP_AGE.days.ago.to_time.strftime('%Y-%m-%d')}.zip") 82 | file.destroy if file 83 | 84 | true 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /spec/support/capybara_drivers.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | Stopped to use 'poltergeist' for headless test. 3 | because I can't resolve below. 4 | 5 | Poltergeist detected another element with CSS selector 'html body.phone div.modal-backdrop.fade.in' at this position. It may be overlapping the element you are trying to interact with. If you don't care about overlapping elements, try using node.trigger('click'). 6 | =end 7 | 8 | Capybara.register_driver :chrome do |app| 9 | Capybara::Selenium::Driver.new(app, browser: :chrome) 10 | end 11 | 12 | Capybara.register_driver :safari do |app| 13 | Capybara::Selenium::Driver.new(app, browser: :safari) 14 | end 15 | 16 | Capybara.register_driver :firefox do |app| 17 | Capybara::Selenium::Driver.new(app, browser: :firefox) 18 | end 19 | 20 | Capybara.register_driver :ie do |app| 21 | Capybara::Selenium::Driver.new app, 22 | browser: :remote, 23 | url: 'http://10.0.1.8:4444/wd/hub', 24 | desired_capabilities: :internet_explorer 25 | end 26 | 27 | Capybara.register_driver :iphone do |app| 28 | Capybara.default_wait_time = 30 29 | Capybara::Selenium::Driver.new app, 30 | browser: :remote, 31 | url: "http://127.0.0.1:4723/wd/hub", 32 | desired_capabilities: { 33 | browserName: 'iphone', 34 | platform: 'Mac', 35 | version: '6.1', 36 | app: 'safari' 37 | } 38 | end 39 | 40 | Capybara.register_driver :ipad do |app| 41 | Capybara.default_wait_time = 30 42 | Capybara::Selenium::Driver.new app, 43 | browser: :remote, 44 | url: "http://127.0.0.1:4723/wd/hub", 45 | desired_capabilities: { 46 | browserName: 'ipad', 47 | platform: 'Mac', 48 | version: '6.1', 49 | app: 'safari', 50 | deviceOrientation: 'landscape' 51 | } 52 | end 53 | 54 | Capybara.register_driver :android do |app| 55 | Capybara.default_wait_time = 30 56 | Capybara::Selenium::Driver.new app, 57 | browser: :remote, 58 | url: "http://127.0.0.1:4723/wd/hub", 59 | desired_capabilities: { 60 | browserName: 'android', 61 | platform: 'Mac', 62 | version: '2.3', 63 | app: 'safari', 64 | deviceOrientation: 'landscape' 65 | } 66 | end 67 | 68 | Capybara.register_driver :phone do |app| 69 | Capybara::Webkit::Driver.new(app) 70 | end 71 | 72 | Capybara.register_driver :tablet do |app| 73 | Capybara::Webkit::Driver.new(app) 74 | end 75 | 76 | if %i(iphone ipad phone tablet ios android).include?(Capybara.javascript_driver) 77 | $el = { 78 | edit_title: '#edit-page-title-phone', 79 | edit_body: '#edit-page-body-phone', 80 | edit_save: '#edit-page-save-phone' 81 | } 82 | RSpec.configure do |config| 83 | config.before :each do 84 | 85 | end 86 | end 87 | else 88 | $el = { 89 | edit_title: '#edit-page-title', 90 | edit_body: '#edit-page-body', 91 | edit_save: '#edit-page-save' 92 | } 93 | end -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | after_create :create_defaults 3 | after_create :generate_export_key 4 | has_one :fb_user, :dependent => :destroy 5 | has_one :gh_user, :dependent => :destroy 6 | has_one :dropbox_user, :dependent => :destroy 7 | has_one :evernote_user, :dependent => :destroy 8 | has_many :pages, :dependent => :destroy 9 | has_many :page_tags, :dependent => :destroy 10 | has_many :messages, :class_name => 'UserMessage', :dependent => :destroy 11 | has_many :properties, :class_name => 'UserProperty', :dependent => :destroy, :extend => PropExtend 12 | has_many :helps, :dependent => :destroy do 13 | def clear(key) 14 | key(key.to_s).destroy_all 15 | end 16 | end 17 | 18 | def to_hash(*args) 19 | { 20 | user_id: self.id, 21 | username: username, 22 | icon_url: icon_url 23 | } 24 | end 25 | 26 | def create_default_helps 27 | helps.create key: 'whatsnew' 28 | helps.create key: 'backup' 29 | helps.create key: 'mobile' 30 | helps.create key: 'hotkey' 31 | helps.create key: 'calendar' 32 | helps.create key: 'upcoming' 33 | helps.create key: 'calendar-external' 34 | helps.create key: 'evernote' 35 | end 36 | 37 | def create_defaults 38 | create_default_helps 39 | properties['autosave'] = true 40 | properties['version'] = WRIPE_VERSION 41 | end 42 | 43 | def merge(u) 44 | status = self.properties['marge_status'].to_s 45 | return false unless ['', 'queued'].include?(status) || status[0, 6] == 'error:' 46 | 47 | if u.evernote_user && self.evernote_user 48 | raise "Cannot merge accounts, both accounts connected with Evernote" 49 | end 50 | 51 | self.properties['marge_status'] = 'marging' 52 | ActiveRecord::Base.transaction do 53 | u.pages(true).each do |page| 54 | page.user = self 55 | page.save! 56 | end 57 | 58 | if u.pages(true).count == 0 59 | u.update_attributes active: false 60 | else 61 | raise "Unknown error" 62 | end 63 | 64 | if self.evernote_user.nil? && u.evernote_user 65 | u.evernote_user.update_attributes user_id: self.id 66 | end 67 | 68 | # send all pages to evernote 69 | if self.evernote_user || u.evernote_user 70 | self.pages(true).each do |page| 71 | page.save_to_evernote 72 | end 73 | end 74 | end 75 | 76 | self.properties['marge_status'] = nil 77 | rescue => e 78 | self.properties['marge_status'] = "error:#{e.message}" 79 | end 80 | 81 | def generate_export_key 82 | @@key_string ||= [('a'..'z'),('A'..'Z')].map{|i| i.to_a}.flatten 83 | string1 = (0...48).map{ @@key_string[rand(@@key_string.length)] }.join 84 | string2 = (0...48).map{ @@key_string[rand(@@key_string.length)] }.join 85 | key = "#{string1}#{self.id % 256}#{string2}#{self.id / 256}" 86 | properties['export-key'] = key 87 | end 88 | 89 | def is_admin? 90 | self.id == 1 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/shared/html2canvas/Queue.js: -------------------------------------------------------------------------------- 1 | function h2cRenderContext(width, height) { 2 | var storage = []; 3 | return { 4 | storage: storage, 5 | width: width, 6 | height: height, 7 | clip: function() { 8 | storage.push({ 9 | type: "function", 10 | name: "clip", 11 | 'arguments': arguments 12 | }); 13 | }, 14 | translate: function() { 15 | storage.push({ 16 | type: "function", 17 | name: "translate", 18 | 'arguments': arguments 19 | }); 20 | }, 21 | fill: function() { 22 | storage.push({ 23 | type: "function", 24 | name: "fill", 25 | 'arguments': arguments 26 | }); 27 | }, 28 | save: function() { 29 | storage.push({ 30 | type: "function", 31 | name: "save", 32 | 'arguments': arguments 33 | }); 34 | }, 35 | restore: function() { 36 | storage.push({ 37 | type: "function", 38 | name: "restore", 39 | 'arguments': arguments 40 | }); 41 | }, 42 | fillRect: function () { 43 | storage.push({ 44 | type: "function", 45 | name: "fillRect", 46 | 'arguments': arguments 47 | }); 48 | }, 49 | createPattern: function() { 50 | storage.push({ 51 | type: "function", 52 | name: "createPattern", 53 | 'arguments': arguments 54 | }); 55 | }, 56 | drawShape: function() { 57 | 58 | var shape = []; 59 | 60 | storage.push({ 61 | type: "function", 62 | name: "drawShape", 63 | 'arguments': shape 64 | }); 65 | 66 | return { 67 | moveTo: function() { 68 | shape.push({ 69 | name: "moveTo", 70 | 'arguments': arguments 71 | }); 72 | }, 73 | lineTo: function() { 74 | shape.push({ 75 | name: "lineTo", 76 | 'arguments': arguments 77 | }); 78 | }, 79 | arcTo: function() { 80 | shape.push({ 81 | name: "arcTo", 82 | 'arguments': arguments 83 | }); 84 | }, 85 | bezierCurveTo: function() { 86 | shape.push({ 87 | name: "bezierCurveTo", 88 | 'arguments': arguments 89 | }); 90 | }, 91 | quadraticCurveTo: function() { 92 | shape.push({ 93 | name: "quadraticCurveTo", 94 | 'arguments': arguments 95 | }); 96 | } 97 | }; 98 | 99 | }, 100 | drawImage: function () { 101 | storage.push({ 102 | type: "function", 103 | name: "drawImage", 104 | 'arguments': arguments 105 | }); 106 | }, 107 | fillText: function () { 108 | storage.push({ 109 | type: "function", 110 | name: "fillText", 111 | 'arguments': arguments 112 | }); 113 | }, 114 | setVariable: function (variable, value) { 115 | storage.push({ 116 | type: "variable", 117 | name: variable, 118 | 'arguments': value 119 | }); 120 | } 121 | }; 122 | } -------------------------------------------------------------------------------- /app/assets/javascripts/shared/utils.js.coffee: -------------------------------------------------------------------------------- 1 | #= require jquery 2 | #= require shared/underscore 3 | 4 | userAgent = window.navigator.userAgent.toLowerCase() 5 | _device_type = 'desktop' 6 | if userAgent.indexOf('ipad') > 0 7 | _device_type = 'tablet' 8 | else if userAgent.indexOf('iphone') > 0 || userAgent.indexOf('ipod') > 0 9 | _device_type = 'phone' 10 | else if userAgent.indexOf('android') > 0 11 | _device_type = 'android' 12 | 13 | win = $(window) 14 | device_type = -> 15 | if _device_type == 'android' || _device_type == 'tablet' 16 | if win.width() > win.height() 17 | 'tablet' 18 | else 19 | 'phone' 20 | else 21 | _device_type 22 | 23 | _is_iphone = userAgent.indexOf('applewebkit') > 0 && userAgent.indexOf('iphone') > 0 24 | is_iphone = -> 25 | _is_iphone 26 | 27 | _is_ios = userAgent.indexOf('applewebkit') > 0 && userAgent.indexOf('mobile') > 0 28 | is_ios = -> 29 | _is_ios 30 | 31 | escape_html = _.escape 32 | 33 | delay = (wait, callback) -> 34 | setTimeout(callback, wait) 35 | 36 | is_app = () -> 37 | !!window.navigator.standalone 38 | # true # debug 39 | 40 | window_height = -> 41 | if is_iphone() && !is_app() 42 | window.innerHeight 43 | else 44 | (document.documentElement.clientHeight || $(window).height()) 45 | 46 | today_string = () -> 47 | today = new Date() 48 | "#{today.getFullYear()}/#{today.getMonth()+1}/#{today.getDate()}" 49 | 50 | resize_el = (el, height, recur) -> 51 | el.height(height) 52 | if !recur && (el[0].clientHeight || el.height()) != height 53 | delay 100, -> resize_el(el, height, true) 54 | 55 | $.fn.isVisible = -> 56 | $.expr.filters.visible(@[0]) 57 | 58 | $.fn.isTextSelected = -> 59 | @[0] && @[0].selectionStart != @[0].selectionEnd 60 | 61 | $.fn.insertToSelection = (ins)-> 62 | if @[0] && @[0].selectionStart != @[0].selectionEnd 63 | val = @val() 64 | sstart = @[0].selectionStart 65 | send = @[0].selectionEnd 66 | line_head = (sstart == 0) || (val.substr(sstart-1, 1) == "\n") 67 | selected = val.substring(sstart, send-1).replace(/\n/g, "\n#{ins}") 68 | str = val.substr(0, sstart) + (if line_head then ins else '') + selected + val.substr(send-1) 69 | @val(str) 70 | @[0].selectionStart = sstart 71 | @[0].selectionEnd = sstart + (if line_head then 1 else 0) + selected.length + 1 72 | 73 | $.fn.removeToSelection = (ins)-> 74 | if @[0] && @[0].selectionStart != @[0].selectionEnd 75 | val = @val() 76 | sstart = @[0].selectionStart 77 | send = @[0].selectionEnd 78 | line_head = (val.substr(sstart-1, 1) == "\n") && (val.substr(sstart, ins.length) == ins) 79 | selected = val.substring(sstart, send-1).replace(new RegExp("\n#{ins}", "g"), "\n") 80 | selected = selected.substr(ins.length) if line_head 81 | str = val.substr(0, sstart) + selected + val.substr(send-1) 82 | @val(str) 83 | @[0].selectionStart = sstart 84 | @[0].selectionEnd = sstart + selected.length + 1 85 | 86 | window.delay = delay 87 | window.device_type = device_type 88 | window.is_iphone = is_iphone 89 | window.is_ios = is_ios 90 | window.escape_html = escape_html 91 | window.today_string = today_string 92 | window.window_height = window_height 93 | window.resize_el = resize_el 94 | window.is_app = is_app 95 | -------------------------------------------------------------------------------- /app/assets/stylesheets/app/navigator.scss: -------------------------------------------------------------------------------- 1 | @import "layout"; 2 | 3 | $logo_height: 48px; 4 | $logo_height_phone: 24px; 5 | 6 | 7 | #navigator-logo { 8 | position: absolute; 9 | top: 15px; 10 | left: 0; 11 | width: $nav_left_width; 12 | color: white; 13 | text-align: center; 14 | font-size: 18px; 15 | } 16 | 17 | #navigator { 18 | box-sizing: border-box; 19 | -moz-box-sizing: border-box; 20 | position: absolute; 21 | top: 0; 22 | left: 0; 23 | width: $nav_left_width; 24 | height: 100%; 25 | margin: 0; 26 | padding: 0; 27 | list-style-type: none; 28 | overflow-y: auto; 29 | overflow-x: visible; 30 | z-index: 100; 31 | background-color: $nav_left_bg_color; 32 | 33 | ul { 34 | margin: $logo_height 0 0 0; 35 | } 36 | 37 | .icon { 38 | color: $nav_left_icon_color; 39 | height: 22px; 40 | display: block; 41 | } 42 | 43 | a { 44 | display: block; 45 | box-sizing: border-box; 46 | -moz-box-sizing: border-box; 47 | text-align: center; 48 | height: $nav_left_width - 12; 49 | width: $nav_left_width; 50 | text-decoration: none; 51 | color: $nav_left_text_color; 52 | padding-top: 9px; 53 | z-index:100; 54 | } 55 | 56 | a span { 57 | display: block; 58 | } 59 | 60 | li.active { 61 | background-color: $nav_left_active_bg_color; 62 | position: relative; 63 | 64 | .icon { 65 | //text-shadow: 1px 1px rgba(0, 0, 0, 0.3); 66 | color: $nav_left_active_icon_color !important; 67 | } 68 | 69 | a:after { 70 | $caret_size: 9px; 71 | display: inline-block; 72 | width: 0; 73 | height: 0; 74 | border-right: $caret_size solid $main_bg_color; 75 | border-top: $caret_size solid transparent; 76 | border-bottom: $caret_size solid transparent; 77 | content: ""; 78 | position: absolute; 79 | right: 0; 80 | top: 29px; 81 | } 82 | 83 | a { 84 | color: $nav_left_active_text_color !important; 85 | //text-shadow: 1px 1px rgba(0, 0, 0, 0.3); 86 | } 87 | } 88 | 89 | li:active { // clicking 90 | background-color: #BDC3C7; 91 | } 92 | } 93 | 94 | .phone { 95 | #navigator-logo { 96 | position: absolute; 97 | top: 4px; 98 | width: $nav_left_width_phone; 99 | font-size: 14px; 100 | } 101 | 102 | #navigator { 103 | width: $nav_left_width_phone !important; 104 | i { 105 | font-size: 2em !important; 106 | } 107 | 108 | ul { 109 | margin: $logo_height_phone 0 32px 0; 110 | } 111 | 112 | a { 113 | width: $nav_left_width_phone !important; 114 | height: $nav_left_width_phone !important; 115 | font-size: 12px; 116 | padding-top: 7px !important; 117 | } 118 | 119 | a span { 120 | width: $nav_left_width_phone !important; 121 | display: block; 122 | } 123 | 124 | .icon { 125 | color: $nav_left_icon_color; 126 | height: 8px; 127 | } 128 | 129 | li.active { 130 | a:after { 131 | $caret_size: 8px; 132 | border-right: $caret_size solid $main_bg_color; 133 | border-top: $caret_size solid transparent; 134 | border-bottom: $caret_size solid transparent; 135 | top: 18px; 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Wripe::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | app_base = URI(ENV['APP_BASE'] || 'https://wri.pe') 4 | Rails.application.routes.default_url_options = { 5 | protocol: app_base.scheme, 6 | host: app_base.host 7 | } 8 | 9 | # Code is not reloaded between requests. 10 | config.cache_classes = true 11 | 12 | # Eager load code on boot. This eager loads most of Rails and 13 | # your application in memory, allowing both thread web servers 14 | # and those relying on copy on write to perform better. 15 | # Rake tasks automatically ignore this option for performance. 16 | config.eager_load = true 17 | 18 | # Full error reports are disabled and caching is turned on. 19 | config.consider_all_requests_local = false 20 | config.action_controller.perform_caching = true 21 | 22 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 23 | # Add `rack-cache` to your Gemfile before enabling this. 24 | # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid. 25 | # config.action_dispatch.rack_cache = true 26 | 27 | # Disable Rails's static asset server (Apache or nginx will already do this). 28 | config.serve_static_assets = true 29 | 30 | # Compress JavaScripts and CSS. 31 | config.assets.js_compressor = :uglifier 32 | # config.assets.css_compressor = :sass 33 | 34 | # Whether to fallback to assets pipeline if a precompiled asset is missed. 35 | config.assets.compile = true 36 | 37 | # Generate digests for assets URLs. 38 | config.assets.digest = true 39 | 40 | # Version of your assets, change this if you want to expire all your assets. 41 | config.assets.version = '1.0' 42 | 43 | # Specifies the header that your server uses for sending files. 44 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 45 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 46 | 47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 48 | # config.force_ssl = true 49 | 50 | # Set to :debug to see everything in the log. 51 | config.log_level = :info 52 | 53 | # Prepend all log lines with the following tags. 54 | # config.log_tags = [ :subdomain, :uuid ] 55 | 56 | # Use a different logger for distributed setups. 57 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 58 | 59 | # Use a different cache store in production. 60 | # config.cache_store = :mem_cache_store 61 | 62 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 63 | #config.action_controller.asset_host = "https://wripe-assets.s3.amazonaws.com" 64 | 65 | # Precompile additional assets. 66 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 67 | # config.assets.precompile += %w( search.js ) 68 | 69 | # Ignore bad email addresses and do not raise email delivery errors. 70 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 71 | # config.action_mailer.raise_delivery_errors = false 72 | 73 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 74 | # the I18n.default_locale when a translation can not be found). 75 | config.i18n.fallbacks = true 76 | 77 | # Send deprecation notices to registered listeners. 78 | config.active_support.deprecation = :notify 79 | 80 | # Disable automatic flushing of the log to improve performance. 81 | # config.autoflush_log = false 82 | 83 | # Use default logging formatter so that PID and timestamp are not suppressed. 84 | config.log_formatter = ::Logger::Formatter.new 85 | end 86 | 87 | Sunspot.config.solr.url = ENV['WEBSOLR_URL'] 88 | -------------------------------------------------------------------------------- /spec/features/modify_page_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | feature 'Modify note', :js => true do 3 | scenario 'load exists note, modify and save' do 4 | user = FactoryGirl.create(:testdrive1) 5 | page = user.pages.create :title => 'TITLE', :body => 'BODY' 6 | 7 | test_login 'testdrive1' 8 | 9 | wait_until do 10 | not find('#list-page').text.blank? 11 | end 12 | 13 | find("#list-page-#{page.key} .title").should have_content("TITLE") 14 | find("#list-page-#{page.key} .title a").click 15 | wait_until_visible $el[:edit_body] 16 | 17 | find($el[:edit_title]).set "TEST NOTE" 18 | find($el[:edit_body]).set "TEST\n123\n" 19 | find($el[:edit_save]).click 20 | 21 | wait_until_visible "#{$el[:edit_save]} span[name='save']" 22 | 23 | user.pages.count.should == 1 24 | user.pages.first.lock_version.should == 1 25 | user.pages.first.title.should == "TEST NOTE" 26 | user.pages.first.body.should == "TEST\n123\n" 27 | 28 | find('#navigator-index').click 29 | wait_until_visible '#list-page' 30 | 31 | wait_and_find("#list-page-#{page.key} .title").should have_content("TEST NOTE") 32 | end 33 | end 34 | 35 | feature 'Modify note and conflict', :js => true do 36 | scenario 'modify and conflict' do 37 | user = FactoryGirl.create(:testdrive1) 38 | page = user.pages.create :title => 'TITLE', :body => 'BODY' 39 | 40 | test_login 'testdrive1' 41 | 42 | wait_until do 43 | not find('#list-page').text.blank? 44 | end 45 | 46 | find("#list-page-#{page.key} .title").should have_content("TITLE") 47 | find("#list-page-#{page.key} .title a").click 48 | 49 | wait_until_visible $el[:edit_body] 50 | find($el[:edit_body]).value.should == 'BODY' 51 | 52 | page.update_attributes body: "BODY\n1\n2\n" 53 | 54 | find($el[:edit_body]).set "BODY\n123\n" 55 | find($el[:edit_save]).click 56 | 57 | wait_until_visible "#{$el[:edit_save]} span[name='save']" 58 | 59 | wait_until_visible '#page-edit-conflict .btn-close' 60 | sleep 1.0 # wait fade out 61 | find('#page-edit-conflict .btn-close').click 62 | sleep 1.0 # wait fade out 63 | 64 | find($el[:edit_body]).value.should == "BODY\n123\n\n1\n2\n\n" 65 | find($el[:edit_save]).click 66 | 67 | wait_until_visible "#{$el[:edit_save]} span[name='save']" 68 | 69 | user.pages.count.should == 1 70 | user.pages.first.lock_version.should == 2 71 | user.pages.first.body.should == "BODY\n123\n\n1\n2\n\n" 72 | end 73 | end 74 | 75 | feature 'Modify note and delay', :js => true do 76 | scenario 'modify and conflict' do 77 | user = FactoryGirl.create(:testdrive1) 78 | page = user.pages.create :title => 'TITLE', :body => 'BODY' 79 | 80 | test_login 'testdrive1' 81 | 82 | wait_until do 83 | not find('#list-page').text.blank? 84 | end 85 | 86 | find("#list-page-#{page.key} .title").should have_content("TITLE") 87 | find("#list-page-#{page.key} .title a").click 88 | 89 | wait_until_visible $el[:edit_body] 90 | find($el[:edit_body]).value.should == 'BODY' 91 | 92 | find($el[:edit_body]).set "BODY\n123\n" 93 | 94 | PagesController._delay = 2.0 95 | find($el[:edit_save]).click 96 | sleep 0.5 97 | 98 | find($el[:edit_body]).value.should == "BODY\n123\n" 99 | find($el[:edit_body]).set "BODY\n1\n2" 100 | 101 | wait_until_visible "#{$el[:edit_save]} span[name='save']" 102 | find($el[:edit_body]).value.should == "BODY\n1\n2" 103 | 104 | user.pages.count.should == 1 105 | user.pages.first.lock_version.should == 1 106 | user.pages.first.body.should == "BODY\n123\n" 107 | 108 | PagesController._delay = nil 109 | find($el[:edit_save]).click 110 | 111 | wait_until_visible "#{$el[:edit_save]} span[name='save']" 112 | find($el[:edit_body]).value.should == "BODY\n1\n2" 113 | 114 | user.pages.count.should == 1 115 | user.pages.first.lock_version.should == 2 116 | user.pages.first.body.should == "BODY\n1\n2" 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /app/assets/stylesheets/app/preview.css.scss: -------------------------------------------------------------------------------- 1 | /* YUI 3.9.1 (build 5852) Copyright 2013 Yahoo! Inc. http://yuilibrary.com/license/ */ 2 | /** 3 | * Percents could work for IE, but for backCompat purposes, we are using keywords. 4 | * x-small is for IE6/7 quirks mode. 5 | */ 6 | #edit-page-preview-body { 7 | font:10px/1.231 arial,helvetica,clean,sans-serif !important; 8 | *font-size:small !important; /* for IE */ 9 | *font:x-small !important; /* for IE in quirks mode */ 10 | background-color: transparent; 11 | 12 | .content { 13 | padding: 6px; 14 | } 15 | 16 | /** 17 | * Nudge down to get to 13px equivalent for these form elements 18 | */ 19 | select, 20 | input, 21 | button, 22 | textarea { 23 | font:99% arial,helvetica,clean,sans-serif; 24 | } 25 | 26 | /** 27 | * To help tables remember to inherit 28 | */ 29 | table { 30 | font-size:inherit; 31 | font:100%; 32 | } 33 | 34 | /** 35 | * Bump up IE to get to 13px equivalent for these fixed-width elements 36 | */ 37 | pre, 38 | code, 39 | kbd, 40 | samp, 41 | tt { 42 | font-family:monospace; 43 | *font-size:108%; 44 | line-height:100%; 45 | } 46 | /* YUI CSS Detection Stamp */ 47 | #yui3-css-stamp.cssfonts { display: none; } 48 | 49 | /* YUI 3.9.1 (build 5852) Copyright 2013 Yahoo! Inc. http://yuilibrary.com/license/ */ 50 | /* base.css, part of YUI's CSS Foundation */ 51 | h1 { 52 | /*18px via YUI Fonts CSS foundation*/ 53 | font-size:138.5%; 54 | line-height:138.5%; 55 | } 56 | 57 | h2 { 58 | /*16px via YUI Fonts CSS foundation*/ 59 | font-size:123.1%; 60 | line-height:123.1%; 61 | } 62 | 63 | h3 { 64 | /*14px via YUI Fonts CSS foundation*/ 65 | font-size:108%; 66 | line-height:108%; 67 | } 68 | 69 | h4, h5, h6 { 70 | /*14px via YUI Fonts CSS foundation*/ 71 | font-size:100%; 72 | line-height:100%; 73 | } 74 | 75 | h1,h2,h3 { 76 | /* top & bottom margin based on font size */ 77 | margin: 0.8em 0 0.2em 0; 78 | } 79 | 80 | h1,h2,h3,h4,h5,h6,strong { 81 | /*bringing boldness back to headers and the strong element*/ 82 | font-weight:bold; 83 | margin: 0.6em 0 0.2em 0; 84 | } 85 | 86 | abbr,acronym { 87 | /*indicating to users that more info is available */ 88 | border-bottom:1px dotted #000; 89 | cursor:help; 90 | } 91 | 92 | em { 93 | /*bringing italics back to the em element*/ 94 | font-style:italic; 95 | } 96 | 97 | blockquote,ul,ol,dl { 98 | /*giving blockquotes and lists room to breath*/ 99 | margin:0; 100 | } 101 | 102 | ol,ul,dl { 103 | /*bringing lists on to the page with breathing room */ 104 | margin-left:0em; 105 | } 106 | 107 | ol { 108 | /*giving OL's LIs generated numbers*/ 109 | list-style: decimal outside; 110 | } 111 | 112 | ul { 113 | /*giving UL's LIs generated disc markers*/ 114 | list-style: disc outside; 115 | } 116 | 117 | dl dd { 118 | /*providing spacing for definition terms*/ 119 | margin-left:1em; 120 | } 121 | 122 | th,td { 123 | /*borders and padding to make the table readable*/ 124 | border:1px solid #000; 125 | padding:.5em; 126 | } 127 | 128 | th { 129 | /*distinguishing table headers from data cells*/ 130 | font-weight:bold; 131 | text-align:center; 132 | } 133 | 134 | caption { 135 | /*coordinated margin to match cell's padding*/ 136 | margin-bottom:.5em; 137 | /*centered so it doesn't blend in to other content*/ 138 | text-align:center; 139 | } 140 | 141 | p,fieldset,table,pre { 142 | /*so things don't run into each other*/ 143 | margin-bottom:1em; 144 | } 145 | 146 | li, ul { 147 | margin-left: 1em; 148 | padding-left: 0em; 149 | } 150 | 151 | blockquote { 152 | border-left: 3px solid #ddd !important; 153 | margin-left: 7px !important; 154 | padding-left: 7px !important; 155 | p { 156 | font:10px/1.231 arial,helvetica,clean,sans-serif !important; 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /spec/features/evernote_sync_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | def signout_evernote 4 | visit 'https://sandbox.evernote.com/Logout.action' 5 | end 6 | 7 | def clear_evernote_data(access_token) 8 | note_store = EvernoteOAuth::Client.new(token: access_token).note_store 9 | notebook = note_store.listNotebooks.select {|notebook| notebook.name == EvernoteUser.notebook_name }.first 10 | if notebook 11 | note_filter = Evernote::EDAM::NoteStore::NoteFilter.new(notebookGuid: notebook.guid) 12 | spec = Evernote::EDAM::NoteStore::NotesMetadataResultSpec.new 13 | note_store.findNotesMetadata(note_filter, 0, 100, spec).notes.each do |note| 14 | note_store.deleteNote(note.guid) 15 | end 16 | end 17 | end 18 | 19 | def login_evernote(evernote_user, clear_data: true) 20 | if clear_data 21 | clear_evernote_data(evernote_user['access_token']) 22 | end 23 | signout_evernote 24 | clear_session 25 | 26 | user = FactoryGirl.create(:testdrive1) 27 | test_login 'testdrive1' 28 | find('#nav-username-link').click 29 | 30 | wait_and_find_css('#settings-evernote-button').click 31 | sleep 1.0 32 | wait_and_find_css('#settings-evernote-turnon-button').click 33 | 34 | within_window 'wripe_auth' do 35 | test_user = EvernoteAuth.config['tests'].first 36 | wait_and_find_css('#username').set test_user['email'] 37 | find('#password').set test_user['password'] 38 | find('#login').click 39 | 40 | wait_and_find_xpath("//input[@name='reauthorize' or @name='authorize']").click 41 | end 42 | 43 | wait_until_visible('#settings-evernote-turnedon') 44 | wait_and_find("#settings-evernote .btn-close").click 45 | sleep 1.0 # wait for face out 46 | 47 | user = User.find_by_username 'testdrive1' 48 | user.pages.should be_empty 49 | user.reload 50 | user 51 | end 52 | 53 | def get_notes(user) 54 | note_store = user.evernote_user.note_store 55 | notebook = note_store.listNotebooks.select {|notebook| notebook.name == EvernoteUser.notebook_name }.first 56 | notebook.should_not be_nil 57 | 58 | note_filter = Evernote::EDAM::NoteStore::NoteFilter.new(notebookGuid: notebook.guid) 59 | spec = Evernote::EDAM::NoteStore::NotesMetadataResultSpec.new 60 | 61 | notes = note_store.findNotesMetadata(note_filter, 0, 100, spec).notes.map do |note| 62 | note_store.getNote(note.guid, true, false, false, false) 63 | end 64 | end 65 | 66 | feature 'Evernote', :js => true do 67 | scenario 'create new note, modify' do 68 | if %i(iphone ipad safari firefox selenium webkit).include?(Capybara.javascript_driver) 69 | pending "#{Capybara.javascript_driver} doesn't support this test" 70 | else 71 | user = login_evernote(EvernoteAuth.config['tests'].first) 72 | 73 | find('#navigator-new').click 74 | wait_until_visible $el[:edit_body] 75 | 76 | # create new note 77 | find($el[:edit_title]).set "EVERNOTE TEST NOTE1" 78 | find($el[:edit_body]).set "# TEST\n123\n" 79 | find($el[:edit_save]).click 80 | 81 | sleep 0.1 82 | wait_until_visible "#{$el[:edit_save]} span[name='save']" 83 | 84 | user.pages.count.should == 1 85 | 86 | notes = get_notes(user) 87 | notes.count.should == 1 88 | note = notes.first 89 | note.title.should == "EVERNOTE TEST NOTE1" 90 | doc = Nokogiri::HTML(note.content) 91 | doc.css('en-note h1').text.should == "TEST" 92 | doc.css('en-note div p').text.should == "123" 93 | 94 | find($el[:edit_title]).set "EVERNOTE TEST NOTE2" 95 | find($el[:edit_body]).set "# TEST2\nABC\n" 96 | find($el[:edit_save]).click 97 | 98 | wait_until_visible "#{$el[:edit_save]} span[name='save']" 99 | 100 | user.pages.count.should == 1 101 | 102 | notes = get_notes(user) 103 | notes.count.should == 1 104 | note = notes.first 105 | note.title.should == "EVERNOTE TEST NOTE2" 106 | doc = Nokogiri::HTML(note.content) 107 | doc.css('en-note h1').text.should == "TEST2" 108 | doc.css('en-note div p').text.should == "ABC" 109 | end 110 | end 111 | end -------------------------------------------------------------------------------- /app/assets/javascripts/app/settings.js.coffee: -------------------------------------------------------------------------------- 1 | #= require shared/underscore 2 | #= require shared/backbone 3 | #= require models/page 4 | #= require shared/defer 5 | #= require shared/labeled_button 6 | #= require shared/modal_dialog 7 | #= require shared/bootstrapSwitch 8 | 9 | class ExportNotesDialog extends ModalDialog 10 | el: $('#settings-export') 11 | 12 | action: (name) -> 13 | if name == 'download' 14 | location.href = '/export/zip' 15 | 16 | class BackupSettingsDialog extends ModalDialog 17 | el: $('#settings-backup') 18 | turned_on_el: $('#settings-backup-dropbox-turnedon') 19 | turned_on_btn_el: $('#settings-backup-dropbox-turnon-button') 20 | turned_off_el: $('#settings-backup-dropbox-turnedoff') 21 | turned_off_btn_el: $('#settings-backup-dropbox-turnoff-button') 22 | processing_el: $('#settings-backup-dropbox-processing') 23 | sign_in_url: '/dropbox_auth/sign_in' 24 | 25 | constructor: -> 26 | super() 27 | @turned_on_btn_el.click (e) => 28 | @processing_el.show() 29 | @turned_on_el.hide() 30 | @turned_off_el.hide() 31 | @auth() 32 | 33 | @turned_off_btn_el.click (e) => 34 | @processing_el.show(); 35 | @turned_on_el.hide(); 36 | @turned_off_el.hide(); 37 | defer = @request_turn_off() 38 | defer.always (data) => 39 | @processing_el.hide() 40 | defer.done (data) => 41 | @turned_on_el.hide() 42 | @turned_off_el.show() 43 | defer.fail(check_auth) 44 | 45 | shown: -> 46 | @processing_el.hide() 47 | @turned_on_el.hide() 48 | @turned_off_el.hide() 49 | @update_status() 50 | 51 | update_status: -> 52 | defer = authorizedRequest(url: "/settings.json") 53 | defer.done (data) => 54 | @processing_el.hide() 55 | if data.use_dropbox 56 | @turned_on_el.show(); 57 | else 58 | @turned_off_el.show(); 59 | defer.fail(check_auth) 60 | 61 | auth: -> 62 | @window = window.open(@sign_in_url, 'wripe_auth') 63 | clearInterval @timer if @timer 64 | @timer = setInterval () => 65 | @check_window() 66 | , 1000 67 | 68 | check_window: -> 69 | if @window && @window.closed 70 | @window = undefined 71 | clearInterval @timer if @timer 72 | @timer = undefined 73 | @update_status() 74 | 75 | hidden: -> 76 | clearInterval @timer if @timer 77 | @timer = undefined 78 | @processing_el.hide() 79 | @turned_on_el.hide() 80 | @turned_off_el.hide() 81 | 82 | request_turn_off: -> 83 | authorizedRequest(url: "/settings.json", type: 'PUT', data: { use_dropbox: false }) 84 | 85 | 86 | class EvernoteSettingsDialog extends BackupSettingsDialog 87 | el: $('#settings-evernote') 88 | turned_on_el: $('#settings-evernote-turnedon') 89 | turned_on_btn_el: $('#settings-evernote-turnon-button') 90 | turned_off_el: $('#settings-evernote-turnedoff') 91 | turned_off_btn_el: $('#settings-evernote-turnoff-button') 92 | processing_el: $('#settings-evernote-processing') 93 | sign_in_url: '/evernote_auth/connect' 94 | 95 | update_status: -> 96 | defer = authorizedRequest(url: "/settings.json") 97 | defer.done (data) => 98 | @processing_el.hide() 99 | if data.use_evernote 100 | @turned_on_el.show(); 101 | else 102 | @turned_off_el.show(); 103 | defer.fail(check_auth) 104 | 105 | 106 | request_turn_off: -> 107 | authorizedRequest(url: "/settings.json", type: 'PUT', data: { use_evernote: false }) 108 | 109 | $ -> 110 | export_dialog = new ExportNotesDialog() 111 | $('#settings-export-button').click -> 112 | export_dialog.show() 113 | 114 | evernote_dialog = new EvernoteSettingsDialog() 115 | $('#help-evernote-button').click -> 116 | evernote_dialog.show() 117 | $('#settings-evernote-button').click -> 118 | evernote_dialog.show() 119 | 120 | backup_dialog = new BackupSettingsDialog() 121 | $('#settings-backup-button').click -> 122 | backup_dialog.show() 123 | $('#settings-backup-button-in-export').click -> 124 | backup_dialog.show() 125 | $('#help-backup-button').click -> 126 | backup_dialog.show() 127 | 128 | -------------------------------------------------------------------------------- /app/models/evernote_user.rb: -------------------------------------------------------------------------------- 1 | class EvernoteUser < ActiveRecord::Base 2 | belongs_to :user 3 | 4 | def self.auth(access_token) 5 | enid, username = EvernoteUser.userinfo_from_access_token(access_token) 6 | 7 | enuser = EvernoteUser.find_by_enid(enid) 8 | if enuser 9 | enuser.user 10 | else 11 | while User.where(:username => username).count > 0 12 | username += rand(10).to_s 13 | end 14 | user = User.create username: username, icon_url: "#{Rails.application.routes.url_helpers.home_url.gsub(/\w+$/,'')}images/evernote_icon128.png" 15 | EvernoteUser.create access_token: access_token, enid: enid, evernote_username: username, user_id: user.id 16 | user 17 | end 18 | end 19 | 20 | class AlreadyConnected < Exception; end 21 | def self.connect(access_token, user) 22 | enid, username = EvernoteUser.userinfo_from_access_token(access_token) 23 | 24 | enuser = EvernoteUser.find_by_enid(enid) 25 | if enuser 26 | if enuser.user_id == user.id 27 | enuser.update_attributes access_token: access_token, evernote_username: username 28 | else 29 | raise AlreadyConnected 30 | end 31 | else 32 | EvernoteUser.create access_token: access_token, enid: enid, evernote_username: username, user_id: user.id 33 | end 34 | 35 | true 36 | end 37 | 38 | def save_to_evernote(page) 39 | if page.properties['evernote-guid'] 40 | update_evernote(page) 41 | else 42 | create_evernote(page) 43 | end 44 | end 45 | 46 | def update_evernote(page) 47 | note = note_store.getNote(page.properties['evernote-guid'], true, false, false, false) 48 | note.title = page.title.strip 49 | note.content = <<__XML__ 50 | 51 | 52 |

    Edit this page in wri.pe


    #{page.body_html}
    53 | __XML__ 54 | note.attributes.contentClass = 'masuidrive.wripe' 55 | note_store.updateNote(note) 56 | note 57 | rescue Evernote::EDAM::Error::EDAMNotFoundException 58 | create_evernote(page) 59 | end 60 | 61 | def create_evernote(page) 62 | note = Evernote::EDAM::Type::Note.new 63 | note.title = page.title.strip 64 | note.content = <<__XML__ 65 | 66 | 67 |

    Edit this page in wri.pe


    #{page.body_html}
    68 | __XML__ 69 | note.tagNames = ['wri.pe'] 70 | note.notebookGuid = notebook_guid || default_notebook_guid 71 | attribs = Evernote::EDAM::Type::NoteAttributes.new 72 | attribs.contentClass = 'masuidrive.wripe' 73 | note.attributes = attribs 74 | begin 75 | note = note_store.createNote(note) 76 | rescue Evernote::EDAM::Error::EDAMNotFoundException 77 | note.notebookGuid = default_notebook_guid 78 | note = note_store.createNote(note) 79 | end 80 | page.properties['evernote-guid'] = note.guid 81 | note 82 | end 83 | 84 | def default_notebook_guid 85 | notebook = note_store.listNotebooks.select {|notebook| notebook.name == EvernoteUser.notebook_name }.first 86 | 87 | if notebook.present? 88 | guid = notebook.guid 89 | else 90 | notebook = Evernote::EDAM::Type::Notebook.new 91 | notebook.name = EvernoteUser.notebook_name 92 | guid = note_store.createNotebook(notebook).guid 93 | end 94 | update_attributes notebook_guid: guid 95 | guid 96 | end 97 | 98 | def self.userinfo_from_access_token(access_token) 99 | client = EvernoteAuth.oauth(access_token) 100 | enuser = client.user_store.getUser 101 | [enuser.id, enuser.username] 102 | end 103 | 104 | def client 105 | @client ||= EvernoteAuth.oauth(access_token) 106 | end 107 | 108 | def note_store 109 | @note_store ||= client.note_store 110 | end 111 | 112 | def self.notebook_name 113 | @notebook_name ||= { 114 | development: 'wripe-dev notebook', 115 | test: 'wripe-test notebook', 116 | }[Rails.env.to_sym] || 'wri.pe notebook' 117 | end 118 | end 119 | --------------------------------------------------------------------------------