├── 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 |
2 |
6 |
7 | <% body_block.call %>
8 |
9 |
15 |
--------------------------------------------------------------------------------
/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 |
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 |
16 | <%= link_to(image_tag(f.image.url, width:64, height: 64), f.image.url) if f.image.file? %>
17 |
18 | <% end %>
19 |
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 |
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 |
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: OffUse dropbox
11 |
12 |
13 | Processing Dropbox connection in other window
14 |
15 |
16 | Current status: OnTurn 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 Dropbox backup feature 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: OffConnect 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 |
5 | Save - [Ctrl]+S
6 | Notes - [Shift]+[Ctrl]+I
7 | Insert today - [Shift]+[Ctrl]+T
8 |
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 |
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;l
0;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 |
--------------------------------------------------------------------------------