├── rails ├── import │ └── .keep ├── log │ └── .keep ├── public │ └── .gitkeep ├── .ruby-version ├── app │ ├── models │ │ ├── concerns │ │ │ └── .keep │ │ └── entry.rb │ ├── controllers │ │ ├── concerns │ │ │ └── .keep │ │ ├── sessions_controller.rb │ │ ├── plans_controller.rb │ │ ├── application_controller.rb │ │ ├── users_controller.rb │ │ └── entries_controller.rb │ ├── serializers │ │ ├── entry_serializer.rb │ │ └── user_serializer.rb │ ├── views │ │ ├── user_mailer │ │ │ ├── export_entries.text.erb │ │ │ └── export_entries.html.erb │ │ ├── layouts │ │ │ ├── entry_mailer.text.erb │ │ │ └── entry_mailer.html.erb │ │ └── entry_mailer │ │ │ ├── welcome.text.erb │ │ │ ├── daily.text.erb │ │ │ ├── welcome.html.erb │ │ │ └── daily.html.erb │ ├── helpers │ │ └── entries_helper.rb │ └── mailers │ │ ├── user_mailer.rb │ │ └── entry_mailer.rb ├── vendor │ └── assets │ │ ├── javascripts │ │ └── .keep │ │ └── stylesheets │ │ └── .keep ├── .rspec ├── config │ ├── initializers │ │ ├── figaro.rb │ │ ├── session_store.rb │ │ ├── cookies_serializer.rb │ │ ├── hirefire.rb │ │ ├── mime_types.rb │ │ ├── stripe.rb │ │ ├── filter_parameter_logging.rb │ │ ├── assets.rb │ │ ├── backtrace_silencers.rb │ │ ├── email.rb │ │ ├── wrap_parameters.rb │ │ └── inflections.rb │ ├── environment.rb │ ├── boot.rb │ ├── database.yml │ ├── application.example.yml │ ├── puma.rb │ ├── locales │ │ ├── en.yml │ │ └── devise.en.yml │ ├── secrets.yml │ ├── routes.rb │ ├── application.rb │ └── environments │ │ ├── development.rb │ │ ├── test.rb │ │ └── production.rb ├── Procfile ├── bin │ ├── bundle │ ├── rake │ ├── delayed_job │ ├── rails │ ├── spring │ └── setup ├── db │ ├── migrate │ │ ├── 20141125163856_user_add_trial_end.rb │ │ ├── 20141125162240_user_add_last_email_sent.rb │ │ ├── 20141125161431_user_add_emails_sent.rb │ │ ├── 20141108213612_add_authentication_token_to_users.rb │ │ ├── 20141221035442_add_previous_email_flag.rb │ │ ├── 1_enable_extensions.rb │ │ ├── 20141109011650_create_entries.rb │ │ ├── 20141109001231_add_base_user_fields.rb │ │ ├── 20141122185234_create_delayed_jobs.rb │ │ └── 20141108212318_devise_create_users.rb │ ├── seeds.rb │ └── schema.rb ├── Procfile.local ├── deploy.sh ├── lib │ ├── development_mail_interceptor.rb │ └── tasks │ │ └── entry.rake ├── Rakefile ├── config.ru ├── spec │ ├── models │ │ ├── entry_spec.rb │ │ └── user_spec.rb │ ├── rails_helper.rb │ └── spec_helper.rb ├── .gitignore ├── Gemfile └── README.rdoc ├── ember ├── public │ ├── .gitkeep │ ├── robots.txt │ ├── favicon.ico │ ├── assets │ │ ├── images │ │ │ ├── logo.jpg │ │ │ ├── logo.png │ │ │ └── home │ │ │ │ ├── screen_1.jpg │ │ │ │ ├── screen_2.jpg │ │ │ │ ├── screen_3.jpg │ │ │ │ ├── screen_4.jpg │ │ │ │ └── screen_5.jpg │ │ └── patterns │ │ │ ├── halftone.png │ │ │ ├── ricepaper2.png │ │ │ ├── groovepaper.png │ │ │ ├── ricepaper2_@2X.png │ │ │ ├── low-contrast-linen.png │ │ │ ├── subtle_white_feathers.png │ │ │ └── subtle_white_feathers_@2X.png │ └── crossdomain.xml ├── vendor │ ├── .gitkeep │ └── js │ │ └── pikaday.jquery.js ├── app │ ├── helpers │ │ ├── .gitkeep │ │ ├── pretty-day.js │ │ ├── pretty-month.js │ │ ├── showdown-html.js │ │ ├── pretty-time.js │ │ └── showdown-with-search.js │ ├── models │ │ ├── .gitkeep │ │ ├── entry.js │ │ └── user.js │ ├── routes │ │ ├── .gitkeep │ │ ├── password-reset.js │ │ ├── importer.js │ │ ├── about.js │ │ ├── login.js │ │ ├── protected.js │ │ ├── register.js │ │ ├── plans.js │ │ ├── settings.js │ │ ├── index.js │ │ ├── entries │ │ │ ├── edit.js │ │ │ ├── new.js │ │ │ ├── index.js │ │ │ └── show.js │ │ ├── plan-required.js │ │ └── application.js │ ├── styles │ │ ├── .gitkeep │ │ ├── errors.styl │ │ ├── notify.styl │ │ ├── app.styl │ │ ├── animations.styl │ │ ├── variables.styl │ │ ├── utility.styl │ │ ├── media_queries.styl │ │ ├── bootstrap_custom.styl │ │ ├── entries.styl │ │ ├── users.styl │ │ ├── pikaday.styl │ │ ├── home.styl │ │ └── github-ribbon.styl │ ├── templates │ │ ├── .gitkeep │ │ ├── components │ │ │ ├── .gitkeep │ │ │ └── calendar-date-picker.hbs │ │ ├── slider.hbs │ │ ├── entries │ │ │ ├── edit.hbs │ │ │ ├── new.hbs │ │ │ ├── list.hbs │ │ │ ├── _show.hbs │ │ │ ├── _list_item.hbs │ │ │ ├── show.hbs │ │ │ ├── form.hbs │ │ │ └── index.hbs │ │ ├── entries.hbs │ │ ├── canceled.hbs │ │ ├── login.hbs │ │ ├── register.hbs │ │ ├── widgets │ │ │ └── large-logo.hbs │ │ ├── _footer.hbs │ │ ├── users │ │ │ ├── email-time.hbs │ │ │ ├── register-form.hbs │ │ │ └── login-form.hbs │ │ ├── password-reset.hbs │ │ ├── application.hbs │ │ ├── passwords │ │ │ ├── reset-start-form.hbs │ │ │ └── reset-finish-form.hbs │ │ ├── home-nav.hbs │ │ ├── importer.hbs │ │ ├── site-nav.hbs │ │ ├── plans.hbs │ │ ├── index.hbs │ │ └── about.hbs │ ├── views │ │ ├── .gitkeep │ │ ├── entries │ │ │ ├── form.js │ │ │ └── list.js │ │ ├── index.js │ │ ├── site-nav.js │ │ ├── textarea-autosize.js │ │ ├── users │ │ │ └── email-time.js │ │ └── slider.js │ ├── components │ │ ├── .gitkeep │ │ └── calendar-date-picker.js │ ├── controllers │ │ ├── .gitkeep │ │ ├── user.js │ │ ├── index.js │ │ ├── login.js │ │ ├── entries │ │ │ ├── index.js │ │ │ └── show.js │ │ ├── register.js │ │ ├── password-reset.js │ │ ├── plans.js │ │ ├── importer.js │ │ └── settings.js │ ├── serializers │ │ └── application.js │ ├── mixins │ │ └── reset-scroll.js │ ├── app.js │ ├── transforms │ │ ├── object.js │ │ └── array.js │ ├── router.js │ ├── index.html │ └── adapters │ │ └── application.js ├── tests │ ├── unit │ │ └── .gitkeep │ ├── helpers │ │ ├── resolver.js │ │ └── start-app.js │ ├── test-helper.js │ ├── index.html │ └── .jshintrc ├── .bowerrc ├── .gitignore ├── testem.json ├── .ember-cli ├── .travis.yml ├── .editorconfig ├── bower.json ├── .jshintrc ├── Brocfile.js ├── package.json ├── README.md └── config │ └── environment.js ├── docs ├── logo.png └── feature_frame.sketch ├── .gitignore └── README.md /rails/import/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rails/log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/vendor/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rails/public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/routes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/styles/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/templates/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/tests/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/controllers/user.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rails/.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.1.4 -------------------------------------------------------------------------------- /rails/app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rails/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rails/vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rails/vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rails/.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /ember/app/templates/slider.hbs: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /ember/app/templates/entries/edit.hbs: -------------------------------------------------------------------------------- 1 | {{partial "entries/form"}} -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/dayjot/master/docs/logo.png -------------------------------------------------------------------------------- /ember/app/styles/errors.styl: -------------------------------------------------------------------------------- 1 | .d-error-box 2 | p 3 | color $red -------------------------------------------------------------------------------- /ember/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /ember/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "analytics": false 4 | } 5 | -------------------------------------------------------------------------------- /rails/config/initializers/figaro.rb: -------------------------------------------------------------------------------- 1 | # Figaro.require_keys("PG_PUBLIC_KEY", "PG_PRIVATE_KEY") -------------------------------------------------------------------------------- /rails/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.session_store :disabled -------------------------------------------------------------------------------- /docs/feature_frame.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/dayjot/master/docs/feature_frame.sketch -------------------------------------------------------------------------------- /ember/app/templates/entries/new.hbs: -------------------------------------------------------------------------------- 1 |Hi there,
2 | 3 |4 | Attached to this email is a text file containing all of your entries on DayJot.com. 5 |
6 | 7 |8 | If you have any questions or concerns, please don't hesitate to repy to this email. 9 |
10 | 11 |
12 | Sincerely,
13 |
14 | Your friendly DayJot Robot.
15 |
6 | Reply with entry for date: <%= Time.now.in_time_zone(@user.time_zone).strftime("%Y-%m-%d") %>. 7 |
8 | 9 |
10 | You can always send a new email to <%= "#{@user.email_key}@post.dayjot.com" %> to create a new entry.
11 |
12 | The entry will be created for the current date, and any subsequent emails within the same day will be appended to that entry.
13 |
Welcome to DayJot, the simple, permanent journaling app.
2 | 3 |How's your day going? To get started, simply reply to this email with your first entry.
4 | 5 |A few notes:
6 | 7 |If you have any questions, please don't hesitate to shoot an email to help@dayjot.com.
-------------------------------------------------------------------------------- /rails/db/migrate/20141109001231_add_base_user_fields.rb: -------------------------------------------------------------------------------- 1 | class AddBaseUserFields < ActiveRecord::Migration 2 | def change 3 | add_column :users, :email_times, :hstore 4 | add_column :users, :email_key, :string 5 | 6 | add_column :users, :last_export_time, :datetime 7 | 8 | add_column :users, :plan, :string 9 | add_column :users, :plan_started, :datetime 10 | add_column :users, :plan_canceled, :datetime 11 | add_column :users, :plan_status, :string, default: 'needs_card' 12 | add_column :users, :stripe_customer_id, :string 13 | add_column :users, :last_4_digits, :string 14 | 15 | add_column :users, :time_zone, :string, default: 'US/Pacific' 16 | add_column :users, :status, :string, default: 'active' 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /ember/app/templates/passwords/reset-start-form.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/templates/users/register-form.hbs: -------------------------------------------------------------------------------- 1 | 23 | 24 | -------------------------------------------------------------------------------- /rails/app/views/entry_mailer/daily.html.erb: -------------------------------------------------------------------------------- 1 |Just reply to this email with your entry for <%= Time.now.in_time_zone(@user.time_zone).strftime("%B %-d") %>.
2 | 3 |---
4 | 5 | <% if @user.include_email_memory %> 6 | <% if @show_entry %> 7 |<%= @entry.time_ago(@user) %> ago you wrote...
8 | 9 | <%= markdown(@entry.body) %> 10 | 11 |---
12 | 13 |View Entry: <%= entry_url(@entry.entry_date) %>
14 | 15 | <% else %> 16 |Keep writing...once you have a few entries you'll see a blast from the past show up here!
17 | <% end %> 18 | <% end %> 19 | 20 |Past entries: https://dayjot.com/entries
21 | 22 |Unsubscribe: https://dayjot.com/settings
-------------------------------------------------------------------------------- /ember/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "-Promise", 6 | "moment", 7 | "_", 8 | "StripeCheckout", 9 | "Pikaday", 10 | "$", 11 | "Showdown" 12 | ], 13 | "browser" : true, 14 | "boss" : true, 15 | "curly": true, 16 | "debug": false, 17 | "devel": true, 18 | "eqeqeq": true, 19 | "evil": true, 20 | "forin": false, 21 | "immed": false, 22 | "laxbreak": false, 23 | "newcap": true, 24 | "noarg": true, 25 | "noempty": false, 26 | "nonew": false, 27 | "nomen": false, 28 | "onevar": false, 29 | "plusplus": false, 30 | "regexp": false, 31 | "undef": true, 32 | "sub": true, 33 | "strict": false, 34 | "white": false, 35 | "eqnull": true, 36 | "esnext": true, 37 | "unused": true 38 | } 39 | -------------------------------------------------------------------------------- /ember/app/templates/users/login-form.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rails/app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ActionMailer::Base 2 | default "Message-ID" => lambda {|_| "<#{SecureRandom.uuid}@dayjot.com>"} 3 | default from: "help@dayjot.com" 4 | 5 | def export_entries(user_id) 6 | @user = User.find(user_id) 7 | return unless @user 8 | @user.last_export_time = Time.now 9 | @user.save 10 | 11 | export_text = "" 12 | @user.entries.order("entry_date DESC").each do |e| 13 | export_text += "#{e.entry_date.strftime("%Y-%m-%d")}\n" 14 | export_text += e.body 15 | export_text += "\n\n" 16 | end 17 | 18 | filename = "dayjot_entries_#{Time.now.to_i}.txt" 19 | attachments[filename] = {:mime_type => 'text/plain', :content => export_text} 20 | 21 | mail to: @user.email, subject: "Your DayJot Entry Export" 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /ember/app/controllers/login.js: -------------------------------------------------------------------------------- 1 | import Ember from "ember"; 2 | 3 | export default Ember.Controller.extend({ 4 | authenticator: 'simple-auth-authenticator:devise', 5 | 6 | identification: null, 7 | password: null, 8 | error: null, 9 | working: false, 10 | 11 | actions: { 12 | authenticate: function() { 13 | var _this = this, 14 | data = this.getProperties('identification', 'password'); 15 | 16 | this.setProperties({ 17 | working: true, 18 | password: null, 19 | error: null 20 | }); 21 | 22 | this.get('session').authenticate('simple-auth-authenticator:devise', data).then(function() { 23 | // authentication was successful 24 | }, function(data) { 25 | _this.set('working', false); 26 | _this.set('error', data.error); 27 | }); 28 | } 29 | } 30 | }); -------------------------------------------------------------------------------- /ember/app/views/site-nav.js: -------------------------------------------------------------------------------- 1 | import Ember from "ember"; 2 | 3 | export default Ember.View.extend({ 4 | templateName: 'site-nav', 5 | tagName: 'nav', 6 | classNames: ['navbar','navbar-default'], 7 | 8 | todayDate: moment().format('YYYY-MM-DD'), 9 | searchTerm: "", 10 | 11 | actions: { 12 | search: function() { 13 | this.get('controller').send('search', this.get('searchTerm')); 14 | }, 15 | toggleDropdown: function() { 16 | var target = this.$('.dropdown-toggle'), 17 | parent = target.parents('li:first'); 18 | 19 | parent.toggleClass('open'); 20 | } 21 | }, 22 | 23 | didInsertElement: function() { 24 | $('body').on('click', function(e) { 25 | if (!$(e.target).hasClass('dropdown-toggle')) { 26 | $('.dropdown-toggle').parents('li:first').removeClass('open'); 27 | } 28 | }); 29 | } 30 | 31 | }); -------------------------------------------------------------------------------- /ember/app/templates/passwords/reset-finish-form.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ember/app/templates/home-nav.hbs: -------------------------------------------------------------------------------- 1 |5 | {{this}} 6 |
7 | {{/each}} 8 |13 | If you have entries stored elsewhere, or have an export .txt file from OhLife, 14 | you can use this tool to add entries to DayJot in bulk. Simple paste 15 | the entry text below, and click "Start Import". Make sure to follow 16 | the formatting guide below. If you have any questions, email 17 | help@dayjot.com. 18 |
19 | 20 |
24 | year-month-day
25 |
26 | Entry Text
27 |
28 | 2011-04-18
29 |
30 | Entry text
31 |
32 | 2011-04-19
33 |
34 | Entry Text
35 |
36 | 2011-04-20
37 |
38 | And so on..
39 |
> {{processingText}}
49 |> Importing {{processingCount}} of {{entryCount}} entries.
50 | 51 | {{#if errors}} 52 | {{{errors}}} 53 | {{/if}} 54 | 55 | {{#if done}} 56 |Done! Successfully added {{successCount}} entries.
57 | {{/if}} 58 |
2 | ### An alternative to [OhLife][ol]
3 |
4 | ## What is it?
5 |
6 | [DayJot][dj] is a simple, private journaling app. It's built to provide many features that were included in the (now defunct) [OhLife][ol] journaling app. The [hosted version][dj] of [DayJot][dj] will be run forever. As long as there is a single user paying $1/month, it will remain online.
7 |
8 | ## What does it do?
9 |
10 | * Customizable email reminder system.
11 | * Reply to reminder to make a post
12 | * Powerful search
13 | * Multiple, beautiful ways to browse your journal.
14 | * Easy [OhLife][ol] importer.
15 | * Easy export of all entries
16 | * Supports Markdown
17 |
18 | ## How do I use it?
19 |
20 | The easiest way is to sign-up at [DayJot][dj] ;).
21 |
22 | To get it running on your own machine, here's what you need to know:
23 |
24 | #### Stack
25 |
26 | * [Ruby 2](https://www.ruby-lang.org/en/)
27 | * [Rails 4](http://rubyonrails.org/)
28 | * [Ember](http://emberjs.com/)
29 | * [Ember-CLI](http://www.ember-cli.com/)
30 | * [PostgreSQL](http://www.postgresql.org/)
31 | * [MailGun](http://mailgun.com/)
32 | * [Delayed Job](https://github.com/collectiveidea/delayed_job)
33 |
34 | #### Requirements
35 |
36 | 1. Install [Node 0.10.x](http://nodejs.org/)
37 | 2. `npm install -g ember-cli`
38 | 3. `npm install -g bower`
39 | 4. Ruby 2.1.4, I recommend installing via [RVM](http://rvm.io)
40 | 5. `gem install foreman`
41 | 5. [PostgreSQL](http://postgresapp.com)
42 |
43 | [This script][dep] provides an easy way to install the above dependencies.
44 |
45 | #### Running [DayJot][dj]
46 |
47 | After you have cloned this repo, and installed the dependencies above, run this setup script to set up your machine with the necessary dependencies to run and test DayJot:
48 |
49 | % cd rails && ./bin/setup
50 |
51 | After setup, you can run the application from the rails directory with:
52 |
53 | % foreman start -f Procfile.local
54 |
55 | You should be set to use the app at `http://localhost:3000`.
56 |
57 | Relevant config variables can be found in the rails/config/application.yml file.
58 |
59 | Depending on your PostgreSQL setup, you may need to update the DB_USERNAME and DB_PASSWORD properties in `rails/config/application.yml`.
60 |
61 | ## How do I Contribute?
62 |
63 | Gee I'm so glad you asked that, great question!
64 |
65 | 1. Fork
66 | 2. Hack
67 | 3. Test (Rails tests can be run via rspec; Tests haven't been setup for Ember)
68 | 4. Send a pull request.
69 |
70 | The biggest issue right now is that there are very few tests (none for Ember and just a skeleton for Rails). Coincidentally, writing tests is one of the best ways to familiarize oneself with a new codebase.
71 |
72 | ### Gift for contributors
73 |
74 | I would be very grateful to anybody who's up for writing tests for this project. In fact, for anybody who submits a PR with one or more tests (one is fine!), I'll set you up on [DayJot][dj] for free, for life.
75 |
76 | ## Contact
77 |
78 | Please direct any questions, concerns, or general chat to hi@dayjot.com. I'm also available on Twitter at [@marbemac][mb].
79 |
80 | ## License
81 |
82 | [DayJot][dj] is released under the GNU V2 License.
83 |
84 | [dj]: https://dayjot.com
85 | [mb]: http://twitter.com/marbemac
86 | [ol]: http://ohlife.com
87 | [dep]: https://github.com/thoughtbot/laptop
88 |
--------------------------------------------------------------------------------
/ember/app/controllers/plans.js:
--------------------------------------------------------------------------------
1 | import Ember from "ember";
2 | import ENV from 'dayjot/config/environment';
3 | import Notify from 'ember-notify';
4 |
5 | export default Ember.Controller.extend({
6 | processing: false,
7 | stripeKey: ENV.APP.STRIPE_KEY,
8 | errorMessage: null,
9 | redirecting: false,
10 | planValue: null,
11 | planId: null,
12 | planText: null,
13 |
14 | hasPlan: function() {
15 | return this.get('model.plan') ? true : false;
16 | }.property('model.plan'),
17 |
18 | isPlanActive: function() {
19 | return this.get('model.planStatus') === 'active' ? true : false;
20 | }.property('model.planStatus'),
21 |
22 | actions: {
23 | startPlan: function() {
24 | this.startPurchase(this.get('planId'), 'DayJot', this.get('planValue') + ' per month');
25 | },
26 | updatePlan: function() {
27 | this.updatePlan(this.get('planId'));
28 | }
29 | },
30 |
31 | startPurchase: function(plan, name, description) {
32 | var _this = this;
33 |
34 | Ember.run.next(function() {
35 | StripeCheckout.open({
36 | key: _this.get('stripeKey'),
37 | name: name,
38 | description: description,
39 | allowRememberMe: false,
40 | email: _this.get('session.currentUser.email'),
41 | panelLabel: "Subscribe",
42 | token: function(result) {
43 | _this.sendPurchase(plan, result.id);
44 | }
45 | });
46 | });
47 | },
48 |
49 | sendPurchase: function(plan, token) {
50 | var _this = this,
51 | user = this.get('session.currentUser');
52 |
53 | this.set('processing', true);
54 | Ember.$.ajax({
55 | url: ENV.APP.API_HOST + "/update_plan",
56 | type: "POST",
57 | data: {plan: plan, token: token},
58 | dataType: 'json',
59 | success: function() {
60 | _this.set('processing', false);
61 | _this.set('redirecting', true);
62 | user.refresh().then(function() {
63 | _this.transitionToRoute('entries');
64 | Notify.success('Successfully subscribed!');
65 | setTimeout(function() {
66 | _this.set('redirecting', false);
67 | }, 1000);
68 | });
69 | },
70 | error: function(error) {
71 | _this.set('processing', false);
72 | _this.set('errorMessage', error.responseJSON.error);
73 | }
74 | });
75 | },
76 |
77 | updatePlan: function(plan) {
78 | var _this = this,
79 | user = this.get('session.currentUser');
80 |
81 | this.set('processing', true);
82 | Ember.$.ajax({
83 | url: ENV.APP.API_HOST + "/update_plan",
84 | type: "POST",
85 | data: {plan: plan},
86 | dataType: 'json',
87 | success: function() {
88 | _this.set('processing', false);
89 | _this.set('redirecting', true);
90 | user.refresh().then(function() {
91 | _this.transitionToRoute('entries');
92 | Notify.success('Subscription updated.');
93 | setTimeout(function() {
94 | _this.set('redirecting', false);
95 | }, 1000);
96 | });
97 | },
98 | error: function(error) {
99 | _this.set('processing', false);
100 | _this.set('errorMessage', error.responseJSON.error);
101 | }
102 | });
103 | }
104 |
105 | });
--------------------------------------------------------------------------------
/rails/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application
18 | # Add `rack-cache` to your Gemfile before enabling this.
19 | # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
20 | # config.action_dispatch.rack_cache = true
21 |
22 | # Disable Rails's static asset server (Apache or nginx will already do this).
23 | config.serve_static_assets = false
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # Generate digests for assets URLs.
33 | config.assets.digest = true
34 |
35 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
36 |
37 | # Specifies the header that your server uses for sending files.
38 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
40 |
41 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
42 | config.force_ssl = true
43 |
44 | # Set to :debug to see everything in the log.
45 | config.log_level = :info
46 |
47 | # Prepend all log lines with the following tags.
48 | # config.log_tags = [ :subdomain, :uuid ]
49 |
50 | # Use a different logger for distributed setups.
51 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
52 |
53 | # Use a different cache store in production.
54 | # config.cache_store = :mem_cache_store
55 |
56 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
57 | # config.action_controller.asset_host = "http://assets.example.com"
58 |
59 | config.action_mailer.default_url_options = { :host => 'https://dayjot.com' }
60 |
61 | # Ignore bad email addresses and do not raise email delivery errors.
62 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
63 | # config.action_mailer.raise_delivery_errors = false
64 |
65 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
66 | # the I18n.default_locale when a translation cannot be found).
67 | config.i18n.fallbacks = true
68 |
69 | # Send deprecation notices to registered listeners.
70 | config.active_support.deprecation = :notify
71 |
72 | # Disable automatic flushing of the log to improve performance.
73 | # config.autoflush_log = false
74 |
75 | # Use default logging formatter so that PID and timestamp are not suppressed.
76 | config.log_formatter = ::Logger::Formatter.new
77 |
78 | # Do not dump schema after migrations.
79 | config.active_record.dump_schema_after_migration = false
80 | end
81 |
--------------------------------------------------------------------------------
/rails/db/schema.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | # This file is auto-generated from the current state of the database. Instead
3 | # of editing this file, please use the migrations feature of Active Record to
4 | # incrementally modify your database, and then regenerate this schema definition.
5 | #
6 | # Note that this schema.rb definition is the authoritative source for your
7 | # database schema. If you need to create the application database on another
8 | # system, you should be using db:schema:load, not running all the migrations
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 | # you'll amass, the slower it'll run and the greater likelihood for issues).
11 | #
12 | # It's strongly recommended that you check this file into your version control system.
13 |
14 | ActiveRecord::Schema.define(version: 20141221035442) do
15 |
16 | # These are extensions that must be enabled in order to support this database
17 | enable_extension "plpgsql"
18 | enable_extension "hstore"
19 | enable_extension "uuid-ossp"
20 |
21 | create_table "delayed_jobs", force: true do |t|
22 | t.integer "priority", default: 0, null: false
23 | t.integer "attempts", default: 0, null: false
24 | t.text "handler", null: false
25 | t.text "last_error"
26 | t.datetime "run_at"
27 | t.datetime "locked_at"
28 | t.datetime "failed_at"
29 | t.string "locked_by"
30 | t.string "queue"
31 | t.datetime "created_at"
32 | t.datetime "updated_at"
33 | end
34 |
35 | add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree
36 |
37 | create_table "entries", id: :uuid, default: "uuid_generate_v4()", force: true do |t|
38 | t.binary "encrypted_body"
39 | t.text "body"
40 | t.date "entry_date"
41 | t.string "source", default: "web"
42 | t.uuid "user_id"
43 | t.datetime "created_at"
44 | t.datetime "updated_at"
45 | end
46 |
47 | add_index "entries", ["user_id", "entry_date"], name: "index_entries_on_user_id_and_entry_date", using: :btree
48 |
49 | create_table "users", id: :uuid, default: "uuid_generate_v4()", force: true do |t|
50 | t.string "email", default: "", null: false
51 | t.string "encrypted_password", default: "", null: false
52 | t.string "reset_password_token"
53 | t.datetime "reset_password_sent_at"
54 | t.datetime "remember_created_at"
55 | t.integer "sign_in_count", default: 0, null: false
56 | t.datetime "current_sign_in_at"
57 | t.datetime "last_sign_in_at"
58 | t.string "current_sign_in_ip"
59 | t.string "last_sign_in_ip"
60 | t.datetime "created_at"
61 | t.datetime "updated_at"
62 | t.string "authentication_token"
63 | t.hstore "email_times"
64 | t.string "email_key"
65 | t.datetime "last_export_time"
66 | t.string "plan"
67 | t.datetime "plan_started"
68 | t.datetime "plan_canceled"
69 | t.string "plan_status", default: "needs_card"
70 | t.string "stripe_customer_id"
71 | t.string "last_4_digits"
72 | t.string "time_zone", default: "US/Pacific"
73 | t.string "status", default: "active"
74 | t.integer "reminder_emails_sent", default: 0
75 | t.datetime "last_reminder_sent_at"
76 | t.datetime "trial_end"
77 | t.boolean "include_email_memory", default: true
78 | end
79 |
80 | add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
81 | add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
82 |
83 | end
84 |
--------------------------------------------------------------------------------
/ember/app/styles/pikaday.styl:
--------------------------------------------------------------------------------
1 | .pika-single
2 | z-index 9999
3 | display block
4 | position relative
5 | color #333
6 | background #fff
7 | border 1px solid #ccc
8 | border-bottom-color #bbb
9 | font-family "Helvetica Neue", Helvetica, Arial, sans-serif
10 |
11 | .pika-single:before,
12 | .pika-single:after
13 | content " "
14 | display table
15 |
16 | .pika-single
17 | *zoom 1
18 | &:after
19 | clear both
20 | &.is-hidden
21 | display none
22 | &.is-bound
23 | position absolute
24 | box-shadow 0 5px 15px -5px rgba(0,0,0,.5)
25 |
26 | .pika-lendar
27 | float left
28 | width 240px
29 | margin 8px
30 |
31 | .pika-title
32 | position relative
33 | text-align center
34 | select
35 | cursor pointer
36 | position absolute
37 | z-index 9998
38 | margin 0
39 | left 0
40 | top 5px
41 | opacity 0
42 |
43 | .pika-label
44 | display inline-block
45 | *display inline
46 | position relative
47 | z-index 9999
48 | overflow hidden
49 | margin 0
50 | padding 5px 3px
51 | font-size 14px
52 | line-height 20px
53 | font-weight bold
54 | background-color #fff
55 |
56 | .pika-prev,
57 | .pika-next
58 | display block
59 | cursor pointer
60 | position relative
61 | outline none
62 | border 0
63 | padding 0
64 | width 20px
65 | height 30px
66 | text-indent 20px
67 | white-space nowrap
68 | overflow hidden
69 | background-color transparent
70 | background-position center center
71 | background-repeat no-repeat
72 | background-size 75% 75%
73 | opacity .5
74 | *position absolute
75 | *top 0
76 |
77 | .pika-prev:hover,
78 | .pika-next:hover
79 | opacity 1
80 |
81 | .pika-prev,
82 | .is-rtl .pika-next
83 | float left
84 | background-image url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==')
85 | *left 0
86 |
87 | .pika-next,
88 | .is-rtl .pika-prev
89 | float right
90 | background-image url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=')
91 | *right 0
92 |
93 | .pika-prev.is-disabled,
94 | .pika-next.is-disabled
95 | cursor default
96 | opacity .2
97 |
98 | .pika-select
99 | display inline-block
100 | *display inline
101 |
102 | .pika-table
103 | width 100%
104 | border-collapse collapse
105 | border-spacing 0
106 | border 0
107 | th
108 | color #999
109 | font-size 12px
110 | line-height 25px
111 | font-weight bold
112 | text-align center
113 |
114 | .pika-table th,
115 | .pika-table td
116 | width 14.285714285714286%
117 | padding 0
118 |
119 | .pika-button
120 | cursor pointer
121 | display block
122 | box-sizing border-box
123 | -moz-box-sizing border-box
124 | outline none
125 | border 0
126 | margin 0
127 | width 100%
128 | padding 5px
129 | color #666
130 | font-size 12px
131 | line-height 15px
132 | text-align right
133 | background #f5f5f5
134 | &:hover
135 | color #fff !important
136 | background #ff8000 !important
137 | box-shadow none !important
138 | border-radius 3px !important
139 |
140 | .pika-week
141 | font-size 11px
142 | color #999
143 |
144 | .is-today
145 | .pika-button
146 | color #33aaff
147 | font-weight bold
148 |
149 | .is-selected
150 | .pika-button
151 | color #fff
152 | font-weight bold
153 | background #33aaff
154 | box-shadow inset 0 1px 3px #178fe5
155 | border-radius 3px
156 |
157 | .is-disabled
158 | .pika-button
159 | pointer-events none
160 | cursor default
161 | color #999
162 | opacity .3
163 |
164 |
--------------------------------------------------------------------------------
/ember/app/styles/home.styl:
--------------------------------------------------------------------------------
1 | .d-home
2 | .row
3 | margin-left 0
4 | margin-right 0
5 | .d-top-half
6 | text-align center
7 | background-image url($contrast-texture-url)
8 | background-color #191919
9 | padding 50px 0
10 | .d-divider
11 | height 20px
12 | box-shadow 0 0 4px #333
13 | background-image url($contrast-texture-url)
14 | background-color $highlight-color
15 | &.muted
16 | height 10px
17 | box-shadow 0 0 1px rgba(0,0,0,0.5)
18 | background-image url($texture-url)
19 | background-color rgba(0,0,0,0.1)
20 | .container
21 | max-width 1200px
22 | width 100%
23 |
24 | .d-123
25 | padding 80px 0
26 | text-align center
27 | .d-name
28 | font-size 22px
29 | font-weight bold
30 | color $primary-color
31 | margin-bottom 10px
32 | p
33 | font-weight 300
34 | margin-bottom 8px
35 | font-size 16px
36 | line-height 22px
37 |
38 |
39 | .d-feature-container
40 | padding 70px 0
41 | background-color rgba(0,0,0,0.04)
42 | img
43 | width 100%
44 | display none
45 | box-shadow 0 0 2px rgba(0,0,0,0.5)
46 | border-radius 7px
47 | &.on
48 | display block
49 | .d-title
50 | font-size 24px
51 | font-weight 300
52 | margin 0 0 60px
53 | text-align center
54 | text-transform uppercase
55 | color rgba(0,0,0,0.8)
56 | .d-features
57 | padding-top 15px
58 | li
59 | opacity 0.3
60 | margin-bottom 50px
61 | cursor pointer
62 | position relative
63 | padding-left 100px
64 | &:last-child
65 | margin-bottom 0
66 | &:hover
67 | opacity 0.75
68 | &.on
69 | opacity 1
70 | cursor default
71 | p
72 | display block
73 | h2
74 | font-size 22px
75 | font-weight bold
76 | margin-bottom 10px
77 | p
78 | font-weight 300
79 | margin-bottom 8px
80 | font-size 16px
81 | line-height 22px
82 | display none
83 | i
84 | border-radius 50%
85 | color white
86 | font-size 18px
87 | margin-top -20px
88 | padding 12px
89 | position absolute
90 | top 50%
91 | left 40px
92 | &.blue
93 | background-color #3498db
94 | &.green
95 | background-color #2ecc71
96 | &.yellow
97 | background-color #f1c40f
98 | &.teal
99 | background-color #1abc9c
100 | &.orange
101 | background-color #e67e22
102 |
103 | .d-open-source
104 | text-align center
105 | padding 80px 0
106 | i
107 | font-size 30px
108 | padding 20px
109 | color white
110 | display inline-block
111 | background-color #34495e
112 | border-radius 50%
113 | p
114 | font-size 22px
115 | margin-top 40px
116 | font-weight 300
117 | color rgba(0,0,0,0.75)
118 | line-height 34px
119 |
120 | .d-home-nav
121 | position relative
122 | z-index 10
123 | margin-top -1 * $header-spacing
124 | .navbar-default
125 | margin-bottom 0
126 | box-shadow none
127 | .navbar-toggle
128 | border-color white
129 | font-size 16px
130 | padding 3px 8px
131 | color white
132 | &:hover
133 | color $action-color
134 | .navbar-header
135 | margin 0
136 | .navbar
137 | background-image url($contrast-texture-url)
138 | background-color #191919
139 | position relative
140 | .d-logo
141 | a
142 | color white
143 | span
144 | background-color white
145 | color $highlight-color
146 | .nav
147 | li
148 | a
149 | color white
150 | &:hover
151 | text-decoration underline
152 | background-color transparent !important
153 | color white
154 | &.active
155 | color white
156 | &:hover
157 | color white
158 | text-decoration none
159 |
--------------------------------------------------------------------------------
/rails/app/controllers/entries_controller.rb:
--------------------------------------------------------------------------------
1 | class EntriesController < ApplicationController
2 | before_action :authenticate_user!, except: [:handle_email]
3 | before_action :set_entry, only: [:show, :update, :destroy]
4 |
5 | def index
6 | @entries = current_user.entries
7 |
8 | if params[:search].present?
9 | @entries = current_user.entries.search_by_body(params[:search])
10 | end
11 |
12 | if params[:when].present?
13 | @entries = @entries.where("to_char(entry_date,'YYYY-MM') = ?", params[:when])
14 | end
15 |
16 | page = params[:page].present? ? params[:page] : 1
17 | @entries = @entries.page(page).per(10)
18 |
19 | render json: @entries.order('entry_date DESC')
20 | end
21 |
22 | def show
23 | unless @entry
24 | entry_date = Time.now.strftime('%Y-%m-%d')
25 | @entry = Entry.new(entry_date: entry_date, user_id: current_user.id)
26 | end
27 | meta = { current_entry: @entry.entry_date, prev_entry: @entry.prev_entry, random_entry: @entry.random_entry, next_entry: @entry.next_entry }
28 |
29 | if @entry.persisted?
30 | render json: { entry: @entry.as_json, meta: meta }
31 | else
32 | render json: { meta: meta }, status: 404
33 | end
34 | end
35 |
36 | def create
37 | @entry = current_user.entries.new(entry_params)
38 | if @entry.save
39 | render json: @entry
40 | else
41 | render json: { errors: @entry.errors.full_messages }, status: :unprocessable_entity
42 | end
43 | end
44 |
45 | def update
46 | @entry.update(entry_params)
47 | render json: @entry
48 | end
49 |
50 | def destroy
51 | @entry.destroy
52 | render json: @entry
53 | end
54 |
55 | def destroy_all
56 | current_user.entries.delete_all
57 | render json: {}
58 | end
59 |
60 | def export
61 | if current_user.can_export?
62 | UserMailer.delay.export_entries(current_user.id)
63 | render json: {}
64 | else
65 | render json: { error: 'You may only export your entries once every 24 hours.' }, status: 400
66 | end
67 | end
68 |
69 | # Handles incoming emails from Mailchimp
70 | def handle_email
71 | token = params['recipient'].split('@')[0]
72 | user = User.where(email_key: token).first
73 |
74 | # TODO: Notify the user that they couldn't be found..
75 | unless user
76 | render json: { error: "User not found for recipient #{params['recipient']} and token #{token}." }
77 | return
78 | end
79 |
80 | # Parse out the entry date from the email
81 | # If not found, use today's date
82 | entry_date_regex = /Reply\ with\ entry\ for\ date:\ ([\d]{4}-[\d]{2}-[\d]{2})\./
83 | m = params['body-plain'].match(entry_date_regex)
84 | if m && m.captures[0]
85 | entry_date = m.captures[0]
86 | else
87 | entry_date = Time.now.in_time_zone(user.time_zone).strftime('%Y-%m-%d')
88 | end
89 |
90 | # Find or create the entry
91 | entry = user.entries.where(entry_date: entry_date).first
92 | unless entry
93 | entry = Entry.new(user_id: user.id, entry_date: entry_date, source: 'email')
94 | end
95 |
96 | # If the entry already exists, append the new content onto the body
97 | if entry.persisted?
98 | entry.body = entry.body + '\n\n' + params['stripped-text']
99 | else
100 | entry.body = params['stripped-text']
101 | end
102 |
103 | if entry.save
104 | render json: entry
105 | else
106 | render json: { errors: entry.errors.full_messages }
107 | end
108 | end
109 |
110 | private
111 |
112 | def set_entry
113 | # if the id is in the format YYYY-MM-DD, search on entry date, else search by id
114 | if /[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}/.match(params[:id])
115 | @entry = current_user.entries.where(entry_date: params[:id]).first
116 | else
117 | @entry = current_user.entries.find(params[:id])
118 | end
119 | end
120 |
121 | def entry_params
122 | params.require(:entry).permit(:body, :entry_date)
123 | end
124 | end
125 |
--------------------------------------------------------------------------------
/ember/app/controllers/importer.js:
--------------------------------------------------------------------------------
1 | import Ember from "ember";
2 |
3 | export default Ember.Controller.extend({
4 | importText: null,
5 | entryCount: null,
6 | done: false,
7 | successCount: 0,
8 | processing: false,
9 | processingText: null,
10 | processingCount: 0,
11 | errors: null,
12 |
13 | actions: {
14 | resetImport: function() {
15 | this.set('importText', "");
16 | this.set('entryCount', null);
17 | this.set('successCount', 0);
18 | this.set('processingText', null);
19 | this.set('processingCount', 0);
20 | this.set('errors', null);
21 | this.set('done', false);
22 | this.set('processing', false);
23 | },
24 | startImport: function() {
25 | this.set('processingText', "Processing import text..");
26 | this.set('processing', true);
27 |
28 | var lines = this.get('importText').split('\n'),
29 | line = null,
30 | entries = [],
31 | currentEntry = null,
32 | entryDate = null,
33 | i = 0;
34 |
35 | for (i = 0; i < lines.length; i++) {
36 | line = lines[i].trim();
37 |
38 | // just a newline
39 | if (!line) {
40 | // if we have an entry right now, add the newline to it
41 | if (currentEntry) {
42 | currentEntry.text = currentEntry.text + "\n";
43 | }
44 | continue;
45 | }
46 |
47 | entryDate = moment(line, 'YYYY-MM-DD', true);
48 | if (entryDate.isValid()) {
49 | if (currentEntry) {
50 | currentEntry.text = $.trim(currentEntry.text);
51 | entries.push(currentEntry);
52 | }
53 |
54 | currentEntry = {
55 | date: entryDate.utc().toDate(),
56 | text: ""
57 | };
58 |
59 | // DailyDiary text exports are formatted as such:
60 | // 2015-01-01
61 | // 22:00
62 | //
63 | // Rest of entry...
64 | //
65 | // So if the next line is a time, skip it to support DailyDiary exports.
66 | if (moment(lines[i+1], 'HH:mm', true).isValid()) {
67 | i++;
68 | }
69 | } else {
70 | if (currentEntry) {
71 | currentEntry.text = currentEntry.text + line + "\n";
72 | }
73 | }
74 | }
75 |
76 | // ran out of lines, need to handle the last entry
77 | // this must happen even if the last line was a newline
78 | if (currentEntry) {
79 | currentEntry.text = $.trim(currentEntry.text);
80 | entries.push(currentEntry);
81 | }
82 |
83 | this.set('entryCount', entries.length);
84 | var entry = null,
85 | entryObjects = [];
86 | for (i = 0; i < entries.length; i++) {
87 | entry = this.store.createRecord('entry', {
88 | entryDate: entries[i].date,
89 | body: entries[i].text
90 | });
91 | entryObjects.push(entry);
92 | }
93 |
94 | this.saveEntries(entryObjects);
95 | }
96 | },
97 |
98 | saveEntries: function(entries) {
99 | var _this = this,
100 | currentEntry = entries.shift();
101 | if (currentEntry) {
102 | currentEntry.save().then(function() {
103 | _this.set('successCount', _this.get('successCount')+1);
104 | }, function(error) {
105 | var errors = _this.get('errors');
106 | errors = errors ? errors : '';
107 | errors += "> "+moment(currentEntry.get('entryDate')).format('YYYY-MM-DD') + ": "+error.error+"
"; 108 | _this.set('errors', errors); 109 | }).finally(function(){ 110 | _this.set('processingCount', _this.get('processingCount')+1); 111 | _this.saveEntries(entries); 112 | }); 113 | } else { 114 | this.set('done', true); 115 | var user = this.get('session.currentUser'); 116 | user.refresh(); 117 | } 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /ember/app/templates/plans.hbs: -------------------------------------------------------------------------------- 1 | {{view "site-nav"}} 2 | 3 |20 | The same price as 21 | {{planText}} 22 |
23 | {{view "slider" startValue=session.currentUser.plan}} 24 | 25 | {{#if session.currentUser}} 26 | {{#if hasPlan}} 27 | {{#if isPlanActive}} 28 | 29 | {{else}} 30 | 31 | {{/if}} 32 | {{else}} 33 | 34 | 35 | {{#if session.currentUser.trialActive}} 36 | {{#link-to "entries" class='btn btn-default'}}... or do this later{{/link-to}} 37 | {{/if}} 38 | 39 | {{/if}} 40 | 41 | {{#if errorMessage}} 42 |{{errorMessage}}
44 |Hmm, looks like your trial is expired!
58 | {{/unless}} 59 | {{/unless}} 60 | {{/if}} 61 |Use DayJot free for a month, no credit card required. {{#link-to "entries"}}Click here{{/link-to}} to carry on.
62 |If you're not happy or something's missing, let us know at hi@dayjot.com
63 |DayJot has an open and up-front business model. You pay money, and we keep your memories online forever.
68 |There's no guess work about how the site will stay up, or whether your data is yours.
69 |DayJot was built to run at minimal cost, and to scale well.
70 |As long as there is a single paying user, DayJot will be online, and this won't happpen.
71 |All credit card info is handled securely with Stripe.
75 |All data sent to and from DayJot is encrypted and stored in a secure location.
76 |The DayJot database itself is encrypted, so even if an attacker got access to it, they could not read the information.
77 |Your information will never be shared or sold.
81 |Only you can view your entries.
82 |No Facebook. No Twitter. By design, DayJot is not a social website.
83 |If you have any questions or issues, please email hi@dayjot.com.
87 |We send you friendly "How'd your day go?" emails.
17 |You reply and write as much as you want.
18 |You end up with a neat collection of your life stories.
19 |Thoughts, ideas, memories.
23 |All saved in one place.
24 |Never shared, only viewable by you.
25 |We'll never get acquired.
29 |We'll never shut down.
30 |You pay. We keep the lights on.
31 |
43 |
44 |
45 |
46 |
47 | 53 | The perfect OhLife replacement, built 54 | to last. Easily import your OhLife entries. 55 |
56 | 57 |61 | Enjoy multiple, beautiful ways to browse your journal. 62 | Skip to random entries, view by month, and more. 63 |
64 | 65 |69 | Powerful search by term and/or month 70 | makes it easy to find that long lost memory. 71 |
72 | 73 |77 | Included in each reminder email is an old entry, 78 | to remind you of that memory from last year. 79 |
80 | 81 |85 | 1-Click export. At any time, we'll email you a text file 86 | with all of your entries. 87 |
88 | 89 |Oh, and DayJot is open source.
101 |
102 | If $1 per month is too rich for you, you can run it yourself!
103 |
1. We'll never get acquired.
17 |2. We'll never shut down.
18 |3. You pay. We keep the lights on.
19 |24 | DayJot is your online, private journal, designed to last. 25 |
26 |27 | Every day, we send you friendly "How'd your day go?" emails. 28 | You reply and write as much as you want. 29 | You end up with a neat collection of your life stories. 30 |
31 |32 | It's permanent, private, and secure, as journals should be. 33 |
34 |35 | Unlike other journal services, we ask that each member pay at least $1 per month to use the service. 36 | Your privacy is paramount. We don't sell your information, and we don't scan your 37 | entries to show you targeted ads. Your data is your data, all we do is 38 | store it for you. A journal service that respects your privacy cannot realistically make money, 39 | other than through direct payment from members. This is why we ask for at least $1. 40 |
41 |46 | Recently, a prominent free online journaling service, OhLife, shutdown. 47 | A more permanent online journaling solution is needed, so that 48 | this doesn't happen. 49 | That said, DayJot is actually inspired by OhLife, so thank you! 50 |
51 |56 | Who am I, you ask? My name is Marc MacLeod, and you can find me on twitter 57 | @marbemac. 58 |
59 | I've 60 | been building websites and apps for over a decade, and created DayJot to fill 61 | the need described above. I built it to run at minimal cost, and to be easy to 62 | extend and maintain. 63 |
64 |65 | I use this product myself, and I'm 100% confident.. ok, 99.99% confident in the pledge 66 | above. If you have any questions or concerns feel free to reach out to me on Twitter, 67 | or at hi@dayjot.com. 68 |
69 |Privacy is very important at DayJot.
74 |Your personal information will never be shared or sold.
75 |All data sent to and from DayJot is encrypted and stored in a secure location.
76 |The DayJot database itself is encrypted, so even if an attacker got access to it, they could not read the information.
77 |No Facebook. No Twitter. No sharing. By design, DayJot is not a social website.
78 |Export your data anytime.
79 |To completely delete your entire account and all associated data, just email help@dayjot.
80 |85 | DayJot is open source software. If you're concerned about security, or just want to run the service on your own private server, I 86 | encourage you to check the code out at https://github.com/marbemac/dayjot. 87 |
88 |89 | Contributions are, of course, welcome :). 90 |
91 | 92 | 93 | 94 |