├── .gitignore ├── .ruby-version ├── Capfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── app ├── assets │ ├── images │ │ ├── .keep │ │ ├── blackboard.jpg │ │ ├── logo.png │ │ ├── panel-top.png │ │ ├── picto-books.png │ │ ├── picto-peerheart.png │ │ ├── picto-plane.png │ │ ├── picto-tasks.png │ │ └── ppcoin.png │ ├── javascripts │ │ ├── application.js.coffee │ │ ├── bootstrap.js.coffee │ │ ├── distribution.js.coffee │ │ ├── home.js.coffee │ │ ├── projects.js.coffee │ │ ├── qrcode │ │ │ ├── jquery.qr.js │ │ │ └── qrcode.js │ │ ├── sessions.js.coffee │ │ └── users.js.coffee │ └── stylesheets │ │ ├── application.css │ │ ├── bootstrap_and_overrides.css.less │ │ ├── distribution.css.sass │ │ ├── flash_message.css.sass │ │ ├── home.css.sass │ │ ├── justified-nav.css │ │ ├── oldsansblack-webfont.eot │ │ ├── oldsansblack-webfont.svg │ │ ├── oldsansblack-webfont.ttf │ │ ├── oldsansblack-webfont.woff │ │ ├── oldsansblack.sass │ │ ├── projects.css.sass │ │ ├── same-height-columns.sass │ │ ├── sessions.css.sass │ │ ├── typeahead.css.sass │ │ └── users.css.sass ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ ├── distributions_controller.rb │ ├── home_controller.rb │ ├── projects_controller.rb │ ├── registrations_controller.rb │ ├── tips_controller.rb │ ├── users │ │ └── omniauth_callbacks_controller.rb │ └── users_controller.rb ├── helpers │ ├── application_helper.rb │ ├── home_helper.rb │ ├── projects_helper.rb │ ├── sessions_helper.rb │ └── users_helper.rb ├── mailers │ └── user_mailer.rb ├── models │ ├── ability.rb │ ├── cold_storage_transfer.rb │ ├── collaborator.rb │ ├── commit.rb │ ├── concerns │ │ └── .keep │ ├── deposit.rb │ ├── distribution.rb │ ├── donation_address.rb │ ├── project.rb │ ├── record_change.rb │ ├── tip.rb │ ├── tipping_policies_text.rb │ └── user.rb └── views │ ├── common │ └── _menu.html.haml │ ├── devise │ ├── confirmations │ │ └── new.html.haml │ ├── mailer │ │ ├── confirmation_instructions.html.haml │ │ ├── reset_password_instructions.html.haml │ │ └── unlock_instructions.html.haml │ ├── passwords │ │ ├── edit.html.haml │ │ └── new.html.haml │ ├── registrations │ │ ├── edit.html.haml │ │ └── new.html.haml │ ├── sessions │ │ └── new.html.haml │ ├── shared │ │ └── _links.haml │ └── unlocks │ │ └── new.html.haml │ ├── distributions │ ├── _form.html.haml │ ├── _reason.html.haml │ ├── _tip_form.html.haml │ ├── edit.html.haml │ ├── index.html.haml │ ├── new.html.haml │ ├── new_recipient_form.html.haml │ └── show.html.haml │ ├── home │ ├── audit.html.haml │ ├── faq.html.md │ └── index.html.haml │ ├── kaminari │ ├── _first_page.html.haml │ ├── _gap.html.haml │ ├── _last_page.html.haml │ ├── _next_page.html.haml │ ├── _page.html.haml │ ├── _paginator.html.haml │ └── _prev_page.html.haml │ ├── layouts │ ├── application.html.haml │ └── closed.html.haml │ ├── projects │ ├── _form.html.haml │ ├── decide_tip_amounts.html.haml │ ├── donate.html.haml │ ├── donors.html.haml │ ├── edit.html.haml │ ├── index.html.haml │ ├── new.html.haml │ ├── show.html.haml │ └── show.svg.erb │ ├── tips │ └── index.html.haml │ ├── user_mailer │ ├── address_request.html.haml │ ├── new_tip.html.haml │ └── security_issue.html.haml │ └── users │ ├── index.html.haml │ ├── set_password_and_address.html.haml │ ├── show.html.haml │ └── update.html.haml ├── bin ├── airbrake ├── bundle ├── bundler ├── cap ├── cdiff ├── colortab ├── decolor ├── erubis ├── haml ├── lessc ├── rackup ├── rails ├── rake ├── rdoc ├── ri ├── sass ├── sass-convert ├── scss ├── sdoc ├── sdoc-merge ├── slimrb ├── sprockets ├── term_display ├── term_mandel ├── thor ├── tilt └── tt ├── config.ru ├── config ├── application.rb ├── boot.rb ├── config.yml.sample ├── cucumber.yml ├── database.yml.sample ├── deploy.rb ├── deploy │ ├── production.rb │ └── staging.rb ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── backtrace_silencers.rb │ ├── commontator.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── errbit.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── intercept_emails.rb │ ├── mime_types.rb │ ├── record_changes.rb │ ├── secret_token.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ ├── devise.en.yml │ ├── en.bootstrap.yml │ └── en.yml ├── routes.rb └── schedule.rb ├── db ├── migrate │ ├── 20131019133109_devise_create_users.rb │ ├── 20131019133235_add_columns_to_users.rb │ ├── 20131019144930_add_bitcoin_address_to_users.rb │ ├── 20131019164745_create_projects.rb │ ├── 20131019170122_create_deposits.rb │ ├── 20131019170659_create_sendmanies.rb │ ├── 20131019170925_create_tips.rb │ ├── 20131019175751_add_some_fields_to_projects.rb │ ├── 20131019205025_add_amount_to_deposit.rb │ ├── 20131019211518_add_last_commit_to_projects.rb │ ├── 20131020003746_add_commit_to_tip.rb │ ├── 20131020120722_add_login_token_to_users.rb │ ├── 20131020143021_add_unsubscribed_to_users.rb │ ├── 20131020145043_add_notified_at_to_users.rb │ ├── 20131030142320_add_project_to_tip.rb │ ├── 20131030142749_drop_deposit_from_tip.rb │ ├── 20131030191346_add_available_amount_cache_to_projects.rb │ ├── 20131212190037_add_cache_to_users.rb │ ├── 20140102095035_add_refunded_at_to_tips.rb │ ├── 20140207061855_add_github_id_to_projects.rb │ ├── 20140209022632_change_projects_description.rb │ ├── 20140209041123_create_indexes_for_projects.rb │ ├── 20140215062842_add_project_to_sendmany.rb │ ├── 20140215094135_add_addres_label_to_project.rb │ ├── 20140215094549_initialize_project_address_label.rb │ ├── 20140309161105_change_project_amount_cache_to_big_int.rb │ ├── 20140309192616_create_collaborators.rb │ ├── 20140323072851_add_hold_tips_to_project.rb │ ├── 20140323165816_add_commit_message_to_tip.rb │ ├── 20140323173320_create_tipping_policies_texts.rb │ ├── 20140330165138_create_cold_storage_transfers.rb │ ├── 20140401174927_add_cold_storage_withdrawal_address_to_project.rb │ ├── 20140402111051_add_disabled_to_project.rb │ ├── 20140403062826_add_account_balance_to_project.rb │ ├── 20140405084351_add_disabled_reason_to_project.rb │ ├── 20140406064344_add_fee_to_sendmany.rb │ ├── 20140406071705_add_fee_to_cold_storage_transfer.rb │ ├── 20140529135156_add_detailed_descrition_to_project.rb │ ├── 20140530132209_rename_sendmany_to_distribution.rb │ ├── 20140531080839_add_sent_at_to_distribution.rb │ ├── 20140601072522_add_devise_confirmable.rb │ ├── 20140601103950_new_default_to_hold_tips.rb │ ├── 20140601104108_remove_unique_constraint_to_project_full_name.rb │ ├── 20140601144116_add_comment_to_tip.rb │ ├── 20140601145337_create_versions.rb │ ├── 20140601145338_add_object_changes_to_versions.rb │ ├── 20140602210025_create_commits.rb │ ├── 20140607100342_add_origin_to_tip.rb │ ├── 20140608120038_install_commontator.commontator.rb │ ├── 20140608131519_rename_origin_to_reason.rb │ ├── 20140609122234_drop_version.rb │ ├── 20140609122440_create_record_changes.rb │ ├── 20140615122107_create_donation_addresses.rb │ ├── 20140615124857_add_donation_address_to_deposit.rb │ ├── 20140616055815_add_user_to_collaborator.rb │ ├── 20140616060504_convert_collaborator_nick_names_to_user.rb │ ├── 20140704060602_add_disabled_to_user.rb │ ├── 20140706075813_remove_git_hub_id_from_project.rb │ ├── 20140714074128_add_identifier_to_user.rb │ └── 20180121184454_add_stake_mint_to_project.rb ├── schema.rb └── seeds.rb ├── features ├── change_github.feature ├── cold_storage.feature ├── commit_from_known_nickname.feature ├── create_project.feature ├── distribute_to_commits.feature ├── distribute_to_user_identifier.feature ├── distribution.feature ├── donate_to_project.feature ├── project_detailed_description.feature ├── step_definitions │ ├── cold_storage.rb │ ├── commit_from_known_nickname.rb │ ├── common.rb │ ├── create_project.rb │ ├── distribution.rb │ ├── donate_to_project.rb │ ├── github.rb │ ├── tip_for_commit.rb │ ├── tip_modifier_interface.rb │ ├── user_identifier.rb │ └── web.rb ├── support │ ├── big_decimal_inspect.rb │ ├── bitcoin_daemon_mock.rb │ ├── env.rb │ ├── factory_girl.rb │ ├── finders.rb │ ├── octokit_mock.rb │ ├── rspec_doubles.rb │ └── to_ostruct.rb ├── tip_for_commit.feature ├── tip_modifier_interface.feature ├── tipping_policies.feature └── user_identifier.feature ├── lib ├── assets │ └── .keep ├── balance_updater.rb ├── bitcoin_address_validator.rb ├── bitcoin_daemon.rb ├── bitcoin_tipper.rb ├── tasks │ ├── .keep │ ├── cucumber.rake │ ├── reassign_noreply_tips.rake │ └── send_security_issue.rake └── templates │ └── haml │ └── scaffold │ └── _form.html.haml ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── robots.txt ├── shield.svg └── webfonts │ ├── 28BE49_0_0.eot │ ├── 28BE49_0_0.ttf │ └── 28BE49_0_0.woff ├── script └── cucumber ├── test ├── controllers │ ├── .keep │ ├── home_controller_test.rb │ ├── projects_controller_test.rb │ ├── sessions_controller_test.rb │ └── users_controller_test.rb ├── factories │ ├── tips.rb │ └── users.rb ├── fixtures │ ├── .keep │ ├── commits.yml │ ├── deposits.yml │ ├── donation_addresses.yml │ ├── projects.yml │ ├── sendmanies.yml │ ├── tipping_policies_texts.yml │ ├── tips.yml │ └── users.yml ├── helpers │ ├── home_helper_test.rb │ ├── projects_helper_test.rb │ ├── sessions_helper_test.rb │ └── users_helper_test.rb ├── integration │ └── .keep ├── mailers │ └── user_mailer_test.rb ├── models │ ├── commit_test.rb │ ├── deposit_test.rb │ ├── donation_address_test.rb │ ├── project_test.rb │ ├── record_change_test.rb │ ├── tip_test.rb │ ├── tipping_policies_text_test.rb │ └── user_test.rb └── test_helper.rb └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.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 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/*.log 16 | /tmp 17 | 18 | /config/database.yml 19 | /config/config.yml 20 | 21 | /public/assets 22 | /screenshot_* 23 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.3 2 | -------------------------------------------------------------------------------- /Capfile: -------------------------------------------------------------------------------- 1 | # Load DSL and Setup Up Stages 2 | require 'capistrano/setup' 3 | 4 | # Includes default deployment tasks 5 | require 'capistrano/deploy' 6 | 7 | # Includes tasks from other gems included in your Gemfile 8 | # 9 | # For documentation on these, see for example: 10 | # 11 | # https://github.com/capistrano/rvm 12 | # https://github.com/capistrano/rbenv 13 | # https://github.com/capistrano/chruby 14 | # https://github.com/capistrano/bundler 15 | # https://github.com/capistrano/rails/tree/master/assets 16 | # https://github.com/capistrano/rails/tree/master/migrations 17 | # 18 | require 'capistrano/rvm' 19 | # require 'capistrano/rbenv' 20 | # require 'capistrano/chruby' 21 | require 'capistrano/bundler' 22 | require 'capistrano/rails/assets' 23 | require 'capistrano/rails/migrations' 24 | 25 | # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. 26 | Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } 27 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 4 | gem 'rails', '~> 4.2.0' 5 | 6 | # Databases 7 | gem 'sqlite3', group: :development 8 | gem 'mysql2', group: :mysql 9 | gem 'pg', '~> 0.20.0', group: :postgresql # fixed version to avoid warning, see https://stackoverflow.com/questions/44607324/installing-newest-version-of-rails-4-with-postgres-the-pgconn-pgresult-and-p 10 | 11 | # Use SCSS for stylesheets 12 | gem 'sass-rails' 13 | gem 'haml-rails' 14 | 15 | # use fork to remove warning, see https://github.com/metaskills/less-rails/issues/122 and https://github.com/metaskills/less-rails/pull/137 16 | gem "less-rails", git: 'https://github.com/brendon/less-rails.git', branch: 'fix-sprockets-loading' 17 | 18 | gem 'twitter-bootstrap-rails', git: 'https://github.com/seyhunak/twitter-bootstrap-rails.git', branch: 'bootstrap3' 19 | 20 | gem 'kaminari' 21 | 22 | # Use Uglifier as compressor for JavaScript assets 23 | gem 'uglifier', '>= 1.3.0' 24 | 25 | # Use CoffeeScript for .js.coffee assets and views 26 | gem 'coffee-rails' 27 | 28 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes 29 | gem 'therubyracer', platforms: :ruby 30 | 31 | # Use jquery as the JavaScript library 32 | gem 'jquery-rails' 33 | 34 | # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks 35 | gem 'turbolinks' 36 | 37 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 38 | gem 'jbuilder', '~> 1.2' 39 | 40 | group :doc do 41 | # bundle exec rake doc:rails generates the API under doc/api. 42 | gem 'sdoc', require: false 43 | end 44 | 45 | gem 'devise', "~> 4.7.1" 46 | gem 'test_after_commit', :group => :test # https://github.com/plataformatec/devise/blob/master/CHANGELOG.md#410 47 | gem 'omniauth' 48 | gem 'omniauth-rails_csrf_protection', '~> 0.1' 49 | gem 'omniauth-github', git: 'https://github.com/alexandrz/omniauth-github.git', branch: 'provide_emails' 50 | gem 'cancancan' 51 | gem 'twitter_bootstrap_form_for', git: 'https://github.com/stouset/twitter_bootstrap_form_for.git' 52 | 53 | gem 'octokit' 54 | 55 | # Use ActiveModel has_secure_password 56 | # gem 'bcrypt-ruby', '~> 3.0.0' 57 | 58 | # Use unicorn as the app server 59 | # gem 'unicorn' 60 | 61 | # Use debugger 62 | # gem 'debugger', group: [:development, :test] 63 | 64 | group :development do 65 | gem 'capistrano', '~> 3.0' 66 | gem 'capistrano-rvm', git: 'https://github.com/capistrano/rvm.git' 67 | gem 'capistrano-bundler', '>= 1.1.0' 68 | gem 'capistrano-rails' 69 | gem 'quiet_assets' 70 | end 71 | 72 | gem 'airbrake' 73 | gem 'httparty' 74 | gem 'whenever' 75 | gem 'rqrcode-rails3' 76 | gem 'exception_notification' 77 | gem 'rack-canonical-host' 78 | gem 'bootstrap_form', '~> 2.3.0' # version 2.4.0 raises a "can't modify frozen string" in gemspec evaluation on old systems 79 | gem 'html_pipeline_rails' 80 | gem 'rails_autolink' 81 | gem 'redcarpet' 82 | gem 'sanitize' 83 | gem 'twitter-typeahead-rails' 84 | gem 'commontator', '~> 4.6.0' 85 | gem 'compass-rails' 86 | 87 | group :test do 88 | gem 'cucumber-rails', :require => false 89 | # database_cleaner is not required, but highly recommended 90 | gem 'database_cleaner' 91 | gem 'rspec-rails' 92 | gem 'factory_girl_rails' 93 | gem 'poltergeist' 94 | gem 'timecop' 95 | gem 'capybara-screenshot' 96 | end 97 | gem 'awesome_print', group: [:development, :test] 98 | gem 'commonmarker' 99 | gem 'rack', '~> 1.6.11' 100 | gem "sprockets", ">= 3.7.2" 101 | gem "ffi", ">= 1.9.24" 102 | gem "loofah", ">= 2.2.3" 103 | gem "rails-html-sanitizer", ">= 1.0.4" 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 sigmike 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Peer4commit 2 | ========== 3 | 4 | [![peercoin tip for next commit](http://peer4commit.com/projects/1.svg)](http://peer4commit.com/projects/1) 5 | [![bitcoin tip for next commit](http://tip4commit.com/projects/560.svg)](http://tip4commit.com/projects/560) 6 | 7 | Donate peercoins to open source projects or make commits and get tips for it. 8 | 9 | Official site: http://peer4commit.com/ 10 | 11 | Development 12 | =========== 13 | 14 | To run peer4commit in development mode follow these instructions: 15 | 16 | * Install [Ruby](https://www.ruby-lang.org/en/downloads/) 2.0+ 17 | 18 | * Install the [bundler](http://bundler.io/) gem (you may need root): 19 | ``` 20 | gem install bundler 21 | ``` 22 | 23 | * Install [git](http://git-scm.com/downloads) 24 | 25 | * Clone the repository 26 | ``` 27 | git clone git@github.com:sigmike/peer4commit.git 28 | cd peer4commit 29 | ``` 30 | 31 | * Install the sqlite3 development libraries 32 | 33 | * Install the gems (without the production gems): 34 | ``` 35 | bundle install --without mysql postgresql 36 | ``` 37 | 38 | * Create `database.yml`. 39 | ``` 40 | cp config/database.yml{.sample,} 41 | ``` 42 | 43 | * Create `config.yml` 44 | ``` 45 | cp config/config.yml{.example,} 46 | ``` 47 | 48 | * Edit `config.yml` 49 | 50 | * Initialize the database 51 | ``` 52 | bundle exec rake db:migrate 53 | ``` 54 | 55 | * Make sure `ppcoind` is running with RPC enabled 56 | 57 | * Run the server 58 | 59 | 60 | bundle exec rails server 61 | 62 | * Connect to the server at http://localhost:3000/ 63 | 64 | 65 | To update the project balances run this command: 66 | ``` 67 | bundle exec rails runner "BalanceUpdater.work" 68 | ``` 69 | 70 | To retreive commits and send tips on project that do not hold tips: 71 | ``` 72 | bundle exec rails runner "BitcoinTipper.work" 73 | ``` 74 | 75 | License 76 | ======= 77 | 78 | [MIT License](https://github.com/sigmike/peer4commit/blob/master/LICENSE) 79 | 80 | Based on [Tip4commit](http://tip4commit.com/), [MIT License](https://github.com/tip4commit/tip4commit/blob/master/LICENSE), copyright (c) 2013-2014 tip4commit 81 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | T4c::Application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/blackboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/images/blackboard.jpg -------------------------------------------------------------------------------- /app/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/images/logo.png -------------------------------------------------------------------------------- /app/assets/images/panel-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/images/panel-top.png -------------------------------------------------------------------------------- /app/assets/images/picto-books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/images/picto-books.png -------------------------------------------------------------------------------- /app/assets/images/picto-peerheart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/images/picto-peerheart.png -------------------------------------------------------------------------------- /app/assets/images/picto-plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/images/picto-plane.png -------------------------------------------------------------------------------- /app/assets/images/picto-tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/images/picto-tasks.png -------------------------------------------------------------------------------- /app/assets/images/ppcoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/images/ppcoin.png -------------------------------------------------------------------------------- /app/assets/javascripts/application.js.coffee: -------------------------------------------------------------------------------- 1 | #= require jquery 2 | #= require jquery_ujs 3 | #= require twitter/bootstrap 4 | #= require turbolinks 5 | #= require twitter/typeahead 6 | #= require_tree . 7 | 8 | $(document).on "turbolinks:load", -> 9 | 10 | if $("body").data("environment") != "test" 11 | # Remove all global properties set by addthis, otherwise it won't reinitialize 12 | for i of window 13 | delete window[i] if /^addthis/.test(i) or /^_at/.test(i) 14 | window.addthis_share = null 15 | 16 | # Finally, load addthis 17 | $.getScript "https://s7.addthis.com/js/250/addthis_widget.js#pubid=ra-526425ac0ea0780b" 18 | -------------------------------------------------------------------------------- /app/assets/javascripts/bootstrap.js.coffee: -------------------------------------------------------------------------------- 1 | jQuery -> 2 | $("a[rel~=popover], .has-popover").popover() 3 | $("a[rel~=tooltip], .has-tooltip").tooltip() 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/distribution.js.coffee: -------------------------------------------------------------------------------- 1 | $(document).on "turbolinks:load", -> 2 | root = $("#add-recipient-panels") 3 | recipients = $("#recipients") 4 | 5 | root.find("form").submit -> 6 | form = $(this) 7 | form.find(".alert").remove() 8 | panel = form.closest(".panel") 9 | $.ajax 10 | type: 'GET' 11 | url: form.attr("action") 12 | data: form.serialize() 13 | success: (data) -> 14 | if data.error 15 | form.append($("
").addClass("alert alert-danger").text(data.error)) 16 | else 17 | recipients.append(data.result) 18 | form[0].reset() 19 | false 20 | error: -> 21 | form.append($("
").addClass("alert alert-danger").text("An error occured.")) 22 | false 23 | 24 | $(".commit-autocomplete[data-project-id]").each -> 25 | input = $(this) 26 | projectId = input.data("project-id") 27 | source = new Bloodhound 28 | datumTokenizer: Bloodhound.tokenizers.obj.whitespace("sha") 29 | queryTokenizer: Bloodhound.tokenizers.whitespace 30 | remote: "/projects/#{projectId}/commit_suggestions.json?query=%QUERY" 31 | 32 | source.initialize() 33 | 34 | input.typeahead {minLength: 2, highlight: true}, 35 | displayKey: "sha" 36 | source: source.ttAdapter() 37 | templates: 38 | suggestion: (object) -> 39 | object.description 40 | 41 | if input.data("submit") 42 | input.bind "typeahead:selected", -> 43 | setTimeout -> 44 | input.closest("form").submit() 45 | input.typeahead('val', '') 46 | , 100 47 | 48 | $(".github-user-autocomplete[data-project-id]").each -> 49 | input = $(this) 50 | projectId = input.data("project-id") 51 | source = new Bloodhound 52 | datumTokenizer: Bloodhound.tokenizers.obj.whitespace("login") 53 | queryTokenizer: Bloodhound.tokenizers.whitespace 54 | remote: "/projects/#{projectId}/github_user_suggestions.json?query=%QUERY" 55 | 56 | source.initialize() 57 | 58 | input.typeahead {minLength: 1, highlight: true}, 59 | displayKey: "login" 60 | source: source.ttAdapter() 61 | templates: 62 | suggestion: (object) -> 63 | object.description 64 | 65 | if input.data("submit") 66 | input.bind "typeahead:selected", -> 67 | setTimeout -> 68 | input.closest("form").submit() 69 | input.typeahead('val', '') 70 | , 100 71 | 72 | $(".user-autocomplete").each -> 73 | input = $(this) 74 | source = new Bloodhound 75 | datumTokenizer: Bloodhound.tokenizers.obj.whitespace("identifier") 76 | queryTokenizer: Bloodhound.tokenizers.whitespace 77 | remote: "/users/suggestions.json?query=%QUERY" 78 | 79 | source.initialize() 80 | 81 | input.typeahead {minLength: 1, highlight: true}, 82 | displayKey: "identifier" 83 | source: source.ttAdapter() 84 | templates: 85 | suggestion: (object) -> 86 | object.description 87 | 88 | if input.data("submit") 89 | input.bind "typeahead:selected", -> 90 | setTimeout -> 91 | input.closest("form").submit() 92 | input.typeahead('val', '') 93 | , 100 94 | -------------------------------------------------------------------------------- /app/assets/javascripts/home.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/projects.js.coffee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/javascripts/projects.js.coffee -------------------------------------------------------------------------------- /app/assets/javascripts/qrcode/jquery.qr.js: -------------------------------------------------------------------------------- 1 | (function( $ ){ 2 | $.fn.qrcode = function(options) { 3 | // if options is string, 4 | if( typeof options === 'string' ){ 5 | options = { text: options }; 6 | } 7 | 8 | // set default values 9 | // typeNumber < 1 for automatic calculation 10 | options = $.extend( {}, { 11 | render : "canvas", 12 | width : 256, 13 | height : 256, 14 | typeNumber : -1, 15 | correctLevel : QRErrorCorrectLevel.H, 16 | background : "#ffffff", 17 | foreground : "#000000" 18 | }, options); 19 | 20 | var createCanvas = function(){ 21 | // create the qrcode itself 22 | var qrcode = new QRCode(options.typeNumber, options.correctLevel); 23 | qrcode.addData(options.text); 24 | qrcode.make(); 25 | 26 | // create canvas element 27 | var canvas = document.createElement('canvas'); 28 | canvas.width = options.width; 29 | canvas.height = options.height; 30 | var ctx = canvas.getContext('2d'); 31 | 32 | // compute tileW/tileH based on options.width/options.height 33 | var tileW = options.width / qrcode.getModuleCount(); 34 | var tileH = options.height / qrcode.getModuleCount(); 35 | 36 | // draw in the canvas 37 | for( var row = 0; row < qrcode.getModuleCount(); row++ ){ 38 | for( var col = 0; col < qrcode.getModuleCount(); col++ ){ 39 | ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background; 40 | var w = (Math.ceil((col+1)*tileW) - Math.floor(col*tileW)); 41 | var h = (Math.ceil((row+1)*tileW) - Math.floor(row*tileW)); 42 | ctx.fillRect(Math.round(col*tileW),Math.round(row*tileH), w, h); 43 | } 44 | } 45 | // return just built canvas 46 | return canvas; 47 | } 48 | 49 | // from Jon-Carlos Rivera (https://github.com/imbcmdth) 50 | var createTable = function(){ 51 | // create the qrcode itself 52 | var qrcode = new QRCode(options.typeNumber, options.correctLevel); 53 | qrcode.addData(options.text); 54 | qrcode.make(); 55 | 56 | // create table element 57 | var $table = $('
') 58 | .css("width", options.width+"px") 59 | .css("height", options.height+"px") 60 | .css("border", "0px") 61 | .css("border-collapse", "collapse") 62 | .css('background-color', options.background); 63 | 64 | // compute tileS percentage 65 | var tileW = options.width / qrcode.getModuleCount(); 66 | var tileH = options.height / qrcode.getModuleCount(); 67 | 68 | // draw in the table 69 | for(var row = 0; row < qrcode.getModuleCount(); row++ ){ 70 | var $row = $('').css('height', tileH+"px").appendTo($table); 71 | 72 | for(var col = 0; col < qrcode.getModuleCount(); col++ ){ 73 | $('') 74 | .css('width', tileW+"px") 75 | .css('background-color', qrcode.isDark(row, col) ? options.foreground : options.background) 76 | .appendTo($row); 77 | } 78 | } 79 | // return just built canvas 80 | return $table; 81 | } 82 | 83 | 84 | return this.each(function(){ 85 | var element = options.render == "canvas" ? createCanvas() : createTable(); 86 | $(element).appendTo(this); 87 | }); 88 | }; 89 | })( jQuery ); -------------------------------------------------------------------------------- /app/assets/javascripts/sessions.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/users.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /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 | *= require rails_bootstrap_forms 12 | *= require_self 13 | *= require_tree . 14 | */ 15 | 16 | 17 | @font-face {font-family: 'Code-Pro-Light-Demo';src: url('/webfonts/28BE49_0_0.eot');src: url('/webfonts/28BE49_0_0.eot?#iefix') format('embedded-opentype'),url('/webfonts/28BE49_0_0.woff') format('woff'),url('/webfonts/28BE49_0_0.ttf') format('truetype');} 18 | .code-pro { font-family: Code-Pro-Light-Demo; } 19 | 20 | .addthis_counter 21 | { 22 | -webkit-box-sizing: content-box; 23 | -moz-box-sizing: content-box; 24 | box-sizing: content-box; 25 | } 26 | .form-devise { 27 | max-width: 430px; 28 | padding: 15px; 29 | margin: 0 auto; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /app/assets/stylesheets/bootstrap_and_overrides.css.less: -------------------------------------------------------------------------------- 1 | @import "twitter/bootstrap/bootstrap"; 2 | 3 | // Set the correct sprite paths 4 | @iconSpritePath: image-url("twitter/bootstrap/glyphicons-halflings.png"); 5 | @iconWhiteSpritePath: image-url("twitter/bootstrap/glyphicons-halflings-white.png"); 6 | 7 | // Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines) 8 | @fontAwesomeEotPath: asset-url("fontawesome-webfont.eot"); 9 | @fontAwesomeEotPath_iefix: asset-url("fontawesome-webfont.eot?#iefix"); 10 | @fontAwesomeWoffPath: asset-url("fontawesome-webfont.woff"); 11 | @fontAwesomeTtfPath: asset-url("fontawesome-webfont.ttf"); 12 | @fontAwesomeSvgPath: asset-url("fontawesome-webfont.svg#fontawesomeregular"); 13 | 14 | // Font Awesome 15 | @import "fontawesome/font-awesome"; 16 | 17 | // Glyphicons 18 | //@import "twitter/bootstrap/sprites.less"; 19 | 20 | // Your custom LESS stylesheets goes here 21 | // 22 | // Since bootstrap was imported above you have access to its mixins which 23 | // you may use and inherit here 24 | // 25 | // If you'd like to override bootstrap's own variables, you can do so here as well 26 | // See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation 27 | // 28 | // Example: 29 | // @linkColor: #ff0000; 30 | 31 | .thread_new_comment { 32 | input { 33 | &:extend(.btn, .btn-default); 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /app/assets/stylesheets/distribution.css.sass: -------------------------------------------------------------------------------- 1 | @import compass/css3 2 | 3 | #distribution-form 4 | td.amount 5 | width: 170px 6 | input 7 | text-align: right 8 | .distribution-action 9 | +inline-block 10 | -------------------------------------------------------------------------------- /app/assets/stylesheets/flash_message.css.sass: -------------------------------------------------------------------------------- 1 | .flash-message 2 | margin-top: 20px 3 | -------------------------------------------------------------------------------- /app/assets/stylesheets/justified-nav.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | border-top: 1px solid #eee; 3 | margin-top: 40px; 4 | padding-top: 40px; 5 | padding-bottom: 40px; 6 | } 7 | 8 | /* Responsive: Portrait tablets and up */ 9 | @media screen and (min-width: 768px) { 10 | /* Remove the padding we set earlier */ 11 | .masthead, 12 | .marketing, 13 | .footer { 14 | padding-left: 0; 15 | padding-right: 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/assets/stylesheets/oldsansblack-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/stylesheets/oldsansblack-webfont.eot -------------------------------------------------------------------------------- /app/assets/stylesheets/oldsansblack-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/stylesheets/oldsansblack-webfont.ttf -------------------------------------------------------------------------------- /app/assets/stylesheets/oldsansblack-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/assets/stylesheets/oldsansblack-webfont.woff -------------------------------------------------------------------------------- /app/assets/stylesheets/oldsansblack.sass: -------------------------------------------------------------------------------- 1 | /* Generated by Font Squirrel (http://www.fontsquirrel.com) on June 13, 2014 2 | 3 | @font-face 4 | font-family: 'oldsansblack' 5 | src: asset-url('oldsansblack-webfont.eot') 6 | src: asset-url('oldsansblack-webfont.eot?#iefix') format("embedded-opentype"), asset-url('oldsansblack-webfont.woff') format("woff"), asset-url('oldsansblack-webfont.ttf') format("truetype"), asset-url('oldsansblack-webfont.svg#oldsansblackregular') format("svg") 7 | font-weight: normal 8 | font-style: normal 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/projects.css.sass: -------------------------------------------------------------------------------- 1 | .commit-sha 2 | font-family: monospace 3 | 4 | .qrcode 5 | text-align: center 6 | 7 | .project-panel 8 | overflow: auto 9 | 10 | .bitcoin-address 11 | word-wrap: break-word 12 | 13 | .donor-list 14 | .amount 15 | text-align: right 16 | 17 | .txid abbr 18 | font-variant: none 19 | text-decoration: none 20 | border-bottom: none 21 | -------------------------------------------------------------------------------- /app/assets/stylesheets/same-height-columns.sass: -------------------------------------------------------------------------------- 1 | .container-xs-height 2 | display: table 3 | padding-left: 0px 4 | padding-right: 0px 5 | 6 | .row-xs-height 7 | display: table-row 8 | 9 | .col-xs-height 10 | display: table-cell 11 | float: none 12 | 13 | @media (min-width: 768px) 14 | .container-sm-height 15 | display: table 16 | padding-left: 0px 17 | padding-right: 0px 18 | .row-sm-height 19 | display: table-row 20 | .col-sm-height 21 | display: table-cell 22 | float: none 23 | 24 | @media (min-width: 992px) 25 | .container-md-height 26 | display: table 27 | padding-left: 0px 28 | padding-right: 0px 29 | .row-md-height 30 | display: table-row 31 | .col-md-height 32 | display: table-cell 33 | float: none 34 | 35 | @media (min-width: 1200px) 36 | .container-lg-height 37 | display: table 38 | padding-left: 0px 39 | padding-right: 0px 40 | .row-lg-height 41 | display: table-row 42 | .col-lg-height 43 | display: table-cell 44 | float: none 45 | 46 | .col-top 47 | vertical-align: top 48 | 49 | .col-middle 50 | vertical-align: middle 51 | 52 | .col-bottom 53 | vertical-align: bottom 54 | -------------------------------------------------------------------------------- /app/assets/stylesheets/sessions.css.sass: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the sessions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/typeahead.css.sass: -------------------------------------------------------------------------------- 1 | .twitter-typeahead 2 | .tt-query, .tt-hint 3 | margin-bottom: 0 4 | 5 | .tt-dropdown-menu 6 | min-width: 160px 7 | margin-top: 2px 8 | padding: 5px 0 9 | background-color: #fff 10 | border: 1px solid #ccc 11 | border: 1px solid rgba(0, 0, 0, 0.2) 12 | *border-right-width: 2px 13 | *border-bottom-width: 2px 14 | -webkit-border-radius: 6px 15 | -moz-border-radius: 6px 16 | border-radius: 6px 17 | -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2) 18 | -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2) 19 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2) 20 | -webkit-background-clip: padding-box 21 | -moz-background-clip: padding 22 | background-clip: padding-box 23 | 24 | .tt-suggestion 25 | display: block 26 | padding: 3px 20px 27 | cursor: pointer 28 | &.tt-is-under-cursor, &:hover 29 | color: #fff 30 | background-color: #0081c2 31 | background-image: -moz-linear-gradient(top, #0088cc, #0077b3) 32 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)) 33 | background-image: -webkit-linear-gradient(top, #0088cc, #0077b3) 34 | background-image: -o-linear-gradient(top, #0088cc, #0077b3) 35 | background-image: linear-gradient(to bottom, #0088cc, #0077b3) 36 | background-repeat: repeat-x 37 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0) 38 | a 39 | color: #fff 40 | p 41 | margin: 0 42 | -------------------------------------------------------------------------------- /app/assets/stylesheets/users.css.sass: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Users controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | #error_explanation 6 | h2 7 | font-size: 16px 8 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | 6 | rescue_from CanCan::AccessDenied do |exception| 7 | if request.xhr? 8 | raise exception 9 | else 10 | redirect_to root_path, :alert => "Access denied" 11 | end 12 | end 13 | 14 | before_filter :configure_permitted_parameters, if: :devise_controller? 15 | 16 | before_filter :closed 17 | 18 | protected 19 | def after_sign_in_path_for(user) 20 | params[:return_url].presence || 21 | session["user_return_to"].presence || 22 | root_path 23 | end 24 | 25 | def configure_permitted_parameters 26 | devise_parameter_sanitizer.permit(:account_update, keys: [:email, :name, :bitcoin_address, :current_password, :password, :password_confirmation]) 27 | end 28 | 29 | def closed 30 | return if controller_name == "home" and action_name == "audit" 31 | 32 | render "layouts/closed", status: 404 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/distributions_controller.rb: -------------------------------------------------------------------------------- 1 | class DistributionsController < ApplicationController 2 | load_and_authorize_resource :project 3 | load_and_authorize_resource :distribution, :through => :project 4 | 5 | def index 6 | @distributions = @distributions.order(created_at: :desc).page(params[:page]).per(30) 7 | end 8 | 9 | def new 10 | end 11 | 12 | def create 13 | @distribution.project = @project 14 | finalize_distribution 15 | if @distribution.save 16 | redirect_to [@project, @distribution], notice: "Distribution created" 17 | else 18 | render "new" 19 | end 20 | end 21 | 22 | def edit 23 | end 24 | 25 | def update 26 | @distribution.attributes = distribution_params 27 | finalize_distribution 28 | if @distribution.save 29 | redirect_to [@project, @distribution], notice: "Distribution updated" 30 | else 31 | render "edit" 32 | end 33 | end 34 | 35 | def show 36 | commontator_thread_show(@distribution) 37 | end 38 | 39 | def send_transaction 40 | @distribution.send_transaction! 41 | redirect_to [@project, @distribution], flash: {notice: "Transaction sent"} 42 | rescue RuntimeError => e 43 | redirect_to [@project, @distribution], flash: {error: e.message} 44 | end 45 | 46 | def new_recipient_form 47 | @tips = [] 48 | if params[:user] and params[:user][:nickname].present? 49 | user = User.enabled.where(nickname: params[:user][:nickname]).first_or_initialize 50 | if user.new_record? 51 | raise "Invalid GitHub user" unless user.valid_github_user? 52 | user.confirm 53 | user.save! 54 | end 55 | @tips << Tip.new(user: user) 56 | elsif params[:user] and params[:user][:identifier].present? 57 | user = User.enabled.find_by(identifier: params[:user][:identifier]) 58 | @tips << Tip.new(user: user) 59 | elsif params[:not_rewarded_commits] 60 | @project.commits.each do |commit| 61 | next if Tip.where(reason: commit).any? 62 | next if Tip.where(commit: commit.sha).where.not(amount: nil).any? 63 | tip = Tip.build_from_commit(commit) 64 | @tips << tip if tip 65 | end 66 | elsif params[:commit] and sha = params[:commit][:sha] 67 | commits = @project.commits.where("sha LIKE ?", "#{sha}%") 68 | count = commits.count 69 | raise "Commit not found" if count == 0 70 | raise "Multiple commits match this prefix" if count > 1 71 | commit = commits.first 72 | @tips << Tip.build_from_commit(commit) 73 | else 74 | raise "Unrecognized recipient" 75 | end 76 | result = render_to_string(layout: false) 77 | render json: {result: result} 78 | rescue RuntimeError => e 79 | render json: {error: e.message} 80 | end 81 | 82 | private 83 | 84 | def distribution_params 85 | if params[:distribution] 86 | params.require(:distribution).permit(tips_attributes: [:id, :coin_amount, :user_id, :comment, :reason_type, :reason_id, :_destroy]) 87 | else 88 | {} 89 | end 90 | end 91 | 92 | def finalize_distribution 93 | @distribution.tips.each do |tip| 94 | tip.project = @project 95 | if tip.user.new_record? 96 | tip.user.skip_confirmation_notification! 97 | end 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | def index 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class RegistrationsController < Devise::RegistrationsController 2 | def update 3 | @user = User.enabled.find(current_user.id) 4 | 5 | user_params = devise_parameter_sanitizer.sanitize(:account_update) 6 | 7 | successfully_updated = if @user.has_password? 8 | @user.update_with_password(user_params) 9 | else 10 | @user.update(user_params) 11 | end 12 | 13 | if successfully_updated 14 | set_flash_message :notice, :updated 15 | # Sign in the user bypassing validation in case their password changed 16 | bypass_sign_in @user 17 | redirect_to after_update_path_for(@user) 18 | else 19 | render "edit" 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/tips_controller.rb: -------------------------------------------------------------------------------- 1 | class TipsController < ApplicationController 2 | def index 3 | if params[:project_id] && @project = Project.find(params[:project_id]) 4 | @tips = @project.tips.includes(:user).order(created_at: :desc).page(params[:page]).per(30) 5 | else 6 | @tips = Tip.includes(:user, :project).order(created_at: :desc).page(params[:page]).per(30) 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /app/controllers/users/omniauth_callbacks_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController 2 | def github 3 | # render text: "#{request.env["omniauth.auth"].to_json}" 4 | info = request.env["omniauth.auth"]["info"] 5 | @user = User.enabled.find_by :nickname => info["nickname"] 6 | if @user.nil? and info["verified_emails"].any? 7 | @user = User.enabled.find_by :email => info["verified_emails"] 8 | end 9 | unless @user 10 | if info['primary_email'] 11 | @user = User.new( 12 | :email => info['primary_email'], 13 | :nickname => info['nickname'] 14 | ) 15 | @user.confirm 16 | @user.save! 17 | else 18 | set_flash_message(:error, :failure, kind: 'GitHub', reason: 'your primary email address should be verified.') 19 | redirect_to new_user_session_path and return 20 | end 21 | end 22 | 23 | @user.name ||= info['name'] 24 | @user.image ||= info['image'] 25 | @user.save 26 | 27 | Collaborator.where(login: @user.nickname, user_id: nil).each do |collaborator| 28 | collaborator.update(user: @user) 29 | end 30 | 31 | sign_in(@user) 32 | redirect_to request.env["omniauth.origin"].presence || after_sign_in_path_for(@user) 33 | set_flash_message(:notice, :success, :kind => "GitHub") if is_navigational_format? 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | before_action except: [:show, :login, :index, :set_password_and_address, :suggestions] do 4 | @user = User.enabled.where(id: params[:id]).first 5 | if current_user 6 | if current_user != @user 7 | redirect_to root_path, alert: "Access denied" 8 | end 9 | else 10 | redirect_to new_user_session_path(return_url: request.url) 11 | end 12 | end 13 | 14 | def show 15 | @user = User.find(params[:id]) 16 | commontator_thread_show(@user) 17 | end 18 | 19 | def index 20 | @users = User.order(withdrawn_amount: :desc, commits_count: :desc).where('commits_count > 0').page(params[:page]).per(30) 21 | end 22 | 23 | def update 24 | if @user.update(users_params) 25 | redirect_to @user, notice: 'Your information was saved.' 26 | else 27 | render :show, alert: 'Error updating peercoin address' 28 | end 29 | end 30 | 31 | def login 32 | @user = User.where(login_token: params[:token]).first 33 | if @user 34 | if params[:unsubscribe] 35 | @user.update unsubscribed: true 36 | flash[:alert] = 'You unsubscribed! Sorry for bothering you. Although, you still can leave us your peercoin address to get your tips.' 37 | end 38 | sign_in_and_redirect @user, event: :authentication 39 | else 40 | redirect_to root_url, alert: 'User not found' 41 | end 42 | end 43 | 44 | def send_tips_back 45 | @user.tips.not_sent.non_refunded.each do |tip| 46 | tip.touch :refunded_at 47 | end 48 | redirect_to @user, notice: 'All your tips have been refunded to their project' 49 | end 50 | 51 | def set_password_and_address 52 | @user = User.enabled.find(params[:id]) 53 | raise "Blank token" if params[:token].blank? 54 | 55 | if @user.confirmed? 56 | redirect_to new_session_path(User), notice: "Your account is already confirmed. Please sign in to set your Peercoin address." 57 | return 58 | end 59 | 60 | raise "Invalid token" unless Devise.secure_compare(params[:token], @user.confirmation_token) 61 | if params[:user] 62 | @user.attributes = params.require(:user).permit(:password, :password_confirmation, :bitcoin_address) 63 | if @user.password.present? and @user.bitcoin_address.present? and @user.save 64 | @user.confirm! 65 | redirect_to root_path, notice: "Information saved" 66 | else 67 | flash.now[:alert] = "Please fill all the information" 68 | end 69 | end 70 | end 71 | 72 | def suggestions 73 | respond_to do |format| 74 | format.json do 75 | query = params[:query] 76 | users = User.enabled.where('identifier LIKE ? OR name LIKE ?', "%#{query}%", "%#{query}%") 77 | users = users.map do |user| 78 | { 79 | identifier: user.identifier, 80 | description: [ 81 | user.name, 82 | "(#{user.identifier})", 83 | ].reject(&:blank?).join(" "), 84 | } 85 | end 86 | render json: users 87 | end 88 | end 89 | end 90 | 91 | private 92 | def users_params 93 | params.require(:user).permit(:bitcoin_address) 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def btc_human amount, options = {} 3 | return nil unless amount 4 | options = (@default_btc_human_options || {}).merge(options) 5 | nobr = options.has_key?(:nobr) ? options[:nobr] : true 6 | currency = options[:currency] || false 7 | precision = options[:precision] || 2 8 | display_currency = options.fetch(:display_currency, true) 9 | btc = "%.#{precision}f" % to_btc(amount) 10 | btc += " PPC" if display_currency 11 | btc = "#{btc}" if currency 12 | btc = "#{btc}" if nobr 13 | btc.html_safe 14 | end 15 | 16 | def with_btc_human_defaults(defaults) 17 | @old_btc_human_defaults ||= [] 18 | @old_btc_human_defaults << @default_btc_human_options 19 | @default_btc_human_options = defaults.dup 20 | yield 21 | @default_btc_human_options = @old_btc_human_defaults.pop 22 | end 23 | 24 | def to_btc satoshies 25 | satoshies.to_d / COIN if satoshies 26 | end 27 | 28 | def transaction_url(txid) 29 | "https://peercoin.mintr.org/tx/#{txid}" 30 | end 31 | 32 | def address_explorers 33 | [:mintr, :blockr] 34 | end 35 | 36 | def address_url(address, explorer = address_explorers.first) 37 | case explorer 38 | when :blockr then "http://ppc.blockr.io/address/info/#{address}" 39 | when :mintr then "https://peercoin.mintr.org/address/#{address}" 40 | else raise "Unknown provider: #{provider.inspect}" 41 | end 42 | end 43 | 44 | def truncate_commit(sha1) 45 | truncate(sha1, length: 10, omission: "") 46 | end 47 | 48 | def commit_tag(sha1) 49 | content_tag(:span, truncate_commit(sha1), class: "commit-sha") 50 | end 51 | 52 | def render_flash_message 53 | html = [] 54 | flash.each do |type, message| 55 | alert_type = case type 56 | when :notice then :success 57 | when :alert, :error then :danger 58 | else type 59 | end 60 | html << content_tag(:div, message, class: "flash-message text-center alert alert-#{alert_type}") 61 | end 62 | html.join("\n").html_safe 63 | end 64 | 65 | def render_markdown(source) 66 | return nil unless source 67 | 68 | markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(safe_links_only: true, filter_html: true), autolink: true) 69 | html = markdown.render(source) 70 | clean = Sanitize.clean(html, Sanitize::Config::RELAXED) 71 | clean.html_safe 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /app/helpers/home_helper.rb: -------------------------------------------------------------------------------- 1 | module HomeHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/projects_helper.rb: -------------------------------------------------------------------------------- 1 | module ProjectsHelper 2 | 3 | def shield_btc_amount amount 4 | btc_amount = to_btc amount 5 | "%.#{6 - btc_amount.to_i.to_s.length}f PPC" % btc_amount 6 | end 7 | 8 | def shield_color project 9 | last_tip = project.tips.order(:created_at).last 10 | if last_tip.nil? || (Time.now - last_tip.created_at > 30.days) 11 | 'red' 12 | elsif (Time.now - last_tip.created_at > 7.days) 13 | 'yellow' 14 | elsif (Time.now - last_tip.created_at > 1.day) 15 | 'yellowgreen' 16 | else 17 | 'green' 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ActionMailer::Base 2 | add_template_helper(ApplicationHelper) 3 | 4 | def new_tip user, tip 5 | @user = user 6 | @tip = tip 7 | 8 | mail to: user.email, subject: "You received a tip for your commit" 9 | end 10 | 11 | def security_issue(user) 12 | @user = user 13 | mail to: user.email, subject: "Security issue on peer4commit.com" 14 | end 15 | 16 | def address_request(tip, collaborator) 17 | @collaborator = collaborator 18 | @tip = tip 19 | @user = tip.user 20 | mail to: @user.email, subject: "[#{tip.project.name}] Provide an address to get your reward" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/models/ability.rb: -------------------------------------------------------------------------------- 1 | class Ability 2 | include CanCan::Ability 3 | 4 | def initialize(user) 5 | can [:read, :donate, :donors], Project 6 | can :read, Distribution 7 | 8 | if user 9 | can [:update, :decide_tip_amounts, :commit_suggestions, :github_user_suggestions], Project, {collaborators: {user_id: user.id}} 10 | can [:create], Project, {collaborators: {user_id: user.id}} 11 | can [:create], Distribution, project: {collaborators: {user_id: user.id}} 12 | can [:update, :new_recipient_form], Distribution, project: {collaborators: {user_id: user.id}}, txid: nil, sent_at: nil 13 | can [:send_transaction], Distribution do |distribution| 14 | distribution.can_be_sent? 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/models/cold_storage_transfer.rb: -------------------------------------------------------------------------------- 1 | class ColdStorageTransfer < ActiveRecord::Base 2 | belongs_to :project 3 | 4 | def confirmed? 5 | confirmations and confirmations >= 1 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/collaborator.rb: -------------------------------------------------------------------------------- 1 | class Collaborator < ActiveRecord::Base 2 | belongs_to :project 3 | belongs_to :user 4 | end 5 | -------------------------------------------------------------------------------- /app/models/commit.rb: -------------------------------------------------------------------------------- 1 | class Commit < ActiveRecord::Base 2 | belongs_to :project 3 | 4 | validates :sha, uniqueness: {scope: :project_id} 5 | end 6 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/deposit.rb: -------------------------------------------------------------------------------- 1 | class Deposit < ActiveRecord::Base 2 | belongs_to :project 3 | belongs_to :donation_address, inverse_of: :deposits 4 | 5 | def fee 6 | (amount * CONFIG["our_fee"]).to_i 7 | end 8 | 9 | def available_amount 10 | [amount - fee, 0].max 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /app/models/distribution.rb: -------------------------------------------------------------------------------- 1 | class Distribution < ActiveRecord::Base 2 | belongs_to :project, inverse_of: :distributions 3 | has_many :tips 4 | accepts_nested_attributes_for :tips, allow_destroy: true 5 | 6 | record_changes(include: :tips) 7 | 8 | acts_as_commontable 9 | 10 | validate :validate_funds 11 | 12 | scope :to_send, -> { where(txid: nil) } 13 | scope :error, -> { where(is_error: true) } 14 | 15 | def sent? 16 | sent_at or txid 17 | end 18 | 19 | def total_amount 20 | tips.map(&:amount).compact.sum 21 | end 22 | 23 | def send_transaction! 24 | Distribution.transaction do 25 | lock! 26 | raise "Already sent" if sent? 27 | raise "Transaction already sent and failed" if is_error? 28 | raise "Project disabled" if project.disabled? 29 | 30 | update!(sent_at: Time.now, is_error: true) # it's a lock to prevent duplicates 31 | end 32 | 33 | data = generate_data 34 | update_attribute(:data, data) 35 | 36 | raise "Not enough funds on Distribution##{id}" if Project.find(project.id).available_amount < 0 37 | 38 | txid = BitcoinDaemon.instance.send_many(project.address_label, JSON.parse(data)) 39 | 40 | update!(txid: txid, is_error: false) 41 | end 42 | 43 | def generate_data 44 | outs = Hash.new { 0.to_d } 45 | tips.each do |tip| 46 | outs[tip.user.bitcoin_address] += tip.amount.to_d / COIN if tip.amount > 0 47 | end 48 | outs.to_json 49 | end 50 | 51 | def all_addresses_known? 52 | tips.all? { |tip| tip.user.try(:bitcoin_address).present? } 53 | end 54 | 55 | def can_be_sent? 56 | !sent? and all_addresses_known? and tips.any? and tips.all?(&:decided?) 57 | end 58 | 59 | def to_label 60 | "##{id} on project #{project.to_label}" 61 | end 62 | 63 | def validate_funds 64 | tips = project.tips.to_a 65 | tips -= self.tips 66 | tips += self.tips 67 | if project.total_deposited < tips.reject(&:refunded?).map(&:amount).compact.sum 68 | errors.add(:base, "Not enough funds") 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /app/models/donation_address.rb: -------------------------------------------------------------------------------- 1 | class DonationAddress < ActiveRecord::Base 2 | belongs_to :project, inverse_of: :donation_addresses 3 | has_many :deposits, inverse_of: :donation_address 4 | 5 | validates :sender_address, bitcoin_address: true, presence: true 6 | validates :donation_address, bitcoin_address: true 7 | 8 | before_create :generate_donation_address 9 | 10 | def generate_donation_address 11 | return if donation_address.present? 12 | raise "The project has no address label" if project.address_label.blank? 13 | self.donation_address = BitcoinDaemon.instance.get_new_address(project.address_label) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/models/record_change.rb: -------------------------------------------------------------------------------- 1 | class RecordChange < ActiveRecord::Base 2 | belongs_to :record, polymorphic: true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/tipping_policies_text.rb: -------------------------------------------------------------------------------- 1 | class TippingPoliciesText < ActiveRecord::Base 2 | belongs_to :project 3 | belongs_to :user 4 | end 5 | -------------------------------------------------------------------------------- /app/views/common/_menu.html.haml: -------------------------------------------------------------------------------- 1 | %nav#main-menu 2 | %ul.nav 3 | %li{class: controller_name == 'home' ? 'active' : ''} 4 | %a{href: root_path} Home 5 | %li{class: controller_name == 'projects' || @project ? 'active' : ''} 6 | %a{href: projects_path} Projects 7 | / %li 8 | / %a{href: "#"} About 9 | / %li 10 | / %a{href: "#"} Contact 11 | -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.haml: -------------------------------------------------------------------------------- 1 | = twitter_bootstrap_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post, class: 'form-devise' }) do |f| 2 | %h2 Resend confirmation instructions 3 | = devise_error_messages! 4 | = f.email_field :email, :autofocus => true 5 | = f.submit "Resend confirmation instructions" 6 | %p 7 | = render "devise/shared/links" 8 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.haml: -------------------------------------------------------------------------------- 1 | %p 2 | Welcome #{@email}! 3 | %p You can confirm your account email through the link below: 4 | %p= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @token) 5 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.haml: -------------------------------------------------------------------------------- 1 | %p 2 | Hello #{@resource.email}! 3 | %p Someone has requested a link to change your password. You can do this through the link below. 4 | %p= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @token) 5 | %p If you didn't request this, please ignore this email. 6 | %p Your password won't change until you access the link above and create a new one. 7 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.haml: -------------------------------------------------------------------------------- 1 | %p 2 | Hello #{@resource.email}! 3 | %p Your account has been locked due to an excessive number of unsuccessful sign in attempts. 4 | %p Click the link below to unlock your account: 5 | %p= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @token) 6 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.haml: -------------------------------------------------------------------------------- 1 | = twitter_bootstrap_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put, class: 'form-devise' }) do |f| 2 | %h2 Change your password 3 | = devise_error_messages! 4 | = f.hidden_field :reset_password_token 5 | = f.password_field :password, :autofocus => true 6 | = f.password_field :password_confirmation 7 | = f.submit "Change my password" 8 | %p 9 | = render "devise/shared/links" 10 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.haml: -------------------------------------------------------------------------------- 1 | = twitter_bootstrap_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post, class: 'form-devise', role: 'form' }) do |f| 2 | %h2 Forgot your password? 3 | = devise_error_messages! 4 | = f.email_field :email, :autofocus => true 5 | = f.submit "Send me reset password instructions" 6 | %p 7 | = render "devise/shared/links" 8 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.haml: -------------------------------------------------------------------------------- 1 | .form-devise 2 | = bootstrap_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put, class: 'form-devise' }) do |f| 3 | %h2 4 | Edit #{resource_name.to_s.humanize} 5 | = devise_error_messages! 6 | = f.static_control :identifier 7 | = f.text_field :name 8 | = f.email_field :email 9 | - if devise_mapping.confirmable? && resource.pending_reconfirmation? 10 | %div 11 | Currently waiting confirmation for: #{resource.unconfirmed_email} 12 | 13 | = f.text_field :bitcoin_address, placeholder: 'Your peercoin address' 14 | %div 15 | = f.password_field :password, autocomplete: 'off', help: "(leave blank if you don't want to change it)" 16 | %div 17 | = f.password_field :password_confirmation, autocomplete: 'off' 18 | - if f.object.has_password? 19 | %div 20 | = f.password_field :current_password, help: "(we need your current password to confirm your changes)" 21 | %div= f.submit "Update", class: 'btn btn-primary btn-block' 22 | 23 | - if @user.balance > 0 24 | %h3 Send tips back 25 | .send-tips-back-block 26 | %p 27 | If you don't want the tips, you can send the funds back to the supported projects: 28 | = button_to "Send all my tips back to their project", send_tips_back_user_path(@user), class: "btn btn-danger btn-block", confirm: "All the #{to_btc @user.balance} peercoins you received will be sent back to their project. Are you sure?" 29 | 30 | %h3 Cancel my account 31 | %p 32 | Unhappy? #{button_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete, class: 'btn btn-danger btn-block'} 33 | = link_to "Back", :back 34 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.haml: -------------------------------------------------------------------------------- 1 | = twitter_bootstrap_form_for(resource, :as => resource_name, :url => registration_path(resource_name), html: { class: 'form-devise'}) do |f| 2 | %h2 Sign up 3 | = devise_error_messages! 4 | = f.email_field :email, :autofocus => true 5 | = f.password_field :password 6 | = f.password_field :password_confirmation 7 | = f.submit "Sign up" 8 | %p 9 | = render "devise/shared/links" 10 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.haml: -------------------------------------------------------------------------------- 1 | #sign-in-form 2 | .row 3 | .col-md-4 4 | %h4 Sign in with your email 5 | = twitter_bootstrap_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| 6 | = hidden_field_tag :return_url, params[:return_url] 7 | = f.email_field :email, :autofocus => true 8 | = f.password_field :password 9 | - if devise_mapping.rememberable? 10 | %div 11 | = f.check_box :remember_me, "Remember me" 12 | = f.submit "Sign in", class: 'btn btn-primary btn-block' 13 | .col-md-4 14 | - if devise_mapping.omniauthable? 15 | %h4 Sign in with a provider 16 | - resource_class.omniauth_providers.each do |provider| 17 | = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider, origin: params[:return_url]), class: "btn btn-primary btn-block", method: :post 18 | .col-md-4 19 | %h4 Other options 20 | = render "devise/shared/links" 21 | -------------------------------------------------------------------------------- /app/views/devise/shared/_links.haml: -------------------------------------------------------------------------------- 1 | - if controller_name != 'sessions' 2 | = link_to "Sign in", new_session_path(resource_name) 3 | %br/ 4 | - if devise_mapping.registerable? && controller_name != 'registrations' 5 | = link_to "Sign up", new_registration_path(resource_name) 6 | %br/ 7 | - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' 8 | = link_to "Forgot your password?", new_password_path(resource_name) 9 | %br/ 10 | - if devise_mapping.confirmable? && controller_name != 'confirmations' 11 | = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) 12 | %br/ 13 | - if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' 14 | = link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) 15 | %br/ 16 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.haml: -------------------------------------------------------------------------------- 1 | %h2 Resend unlock instructions 2 | = form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| 3 | = devise_error_messages! 4 | %div 5 | = f.label :email 6 | %br/ 7 | = f.email_field :email, :autofocus => true 8 | %div= f.submit "Resend unlock instructions" 9 | = render "devise/shared/links" 10 | -------------------------------------------------------------------------------- /app/views/distributions/_form.html.haml: -------------------------------------------------------------------------------- 1 | .row 2 | .col-md-12 3 | = bootstrap_form_for [@project, @distribution], html: {id: "distribution-form"} do |f| 4 | - if (errors = f.object.errors[:base]).any? 5 | .alert.alert-danger 6 | Unable to save the distribution: #{errors.join(", ")} 7 | %table.table 8 | %thead 9 | %th Recipient 10 | %th Reason 11 | %th Amount 12 | %th 13 | %tbody#recipients 14 | - f.object.tips.each do |tip| 15 | = render "tip_form", tip: tip, form: f 16 | .text-center 17 | = f.submit "Save the distribution", class: 'btn btn-primary' 18 | #add-recipient-panels 19 | .row 20 | .col-md-12 21 | %label{for: "add-recipients-input"} Add recipient(s) 22 | .row 23 | .col-md-3 24 | .panel.panel-default 25 | .panel-heading 26 | %h3.panel-title Peer4commit user 27 | .panel-body 28 | .input-group 29 | = bootstrap_form_for User.new, url: new_recipient_form_project_distributions_path(@project) do |f| 30 | = f.text_field :identifier, hide_label: true, append: content_tag(:button, "Add", class: "btn btn-default add-recipient-button"), class: "user-autocomplete", data: {submit: true} 31 | .col-md-3 32 | .panel.panel-default 33 | .panel-heading 34 | %h3.panel-title GitHub user 35 | .panel-body 36 | = bootstrap_form_for User.new, url: new_recipient_form_project_distributions_path(@project) do |f| 37 | = f.text_field :nickname, hide_label: true, append: content_tag(:button, "Add", class: "btn btn-default add-recipient-button"), class: "github-user-autocomplete", data: {project_id: @project.id, submit: true} 38 | .col-md-3 39 | .panel.panel-default 40 | .panel-heading 41 | %h3.panel-title Author of a commit 42 | .panel-body 43 | = bootstrap_form_for Commit.new, url: new_recipient_form_project_distributions_path(@project) do |f| 44 | = f.text_field :sha, hide_label: true, append: content_tag(:button, "Add", class: "btn btn-default add-recipient-button"), class: "commit-autocomplete", data: {project_id: @project.id, submit: true} 45 | .col-md-3 46 | .panel.panel-default 47 | .panel-heading 48 | %h3.panel-title Authors of commits 49 | .panel-body 50 | = bootstrap_form_for User.new, url: new_recipient_form_project_distributions_path(@project) do |f| 51 | = hidden_field_tag :not_rewarded_commits, "1" 52 | %button.btn-block.btn.btn-default Commits not rewarded 53 | -------------------------------------------------------------------------------- /app/views/distributions/_reason.html.haml: -------------------------------------------------------------------------------- 1 | - case tip.reason 2 | - when Commit 3 | - commit = tip.reason 4 | Commit #{link_to truncate_commit(commit.sha), "https://github.com/#{commit.project.full_name}/commit/#{commit.sha}"}: 5 | %pre= commit.message 6 | - when nil 7 | - if tip.commit.present? 8 | - if tip.project 9 | Commit #{link_to truncate_commit(tip.commit), "https://github.com/#{tip.project.full_name}/commit/#{tip.commit}"}: 10 | - else 11 | Commit #{tip.commit} 12 | - if tip.commit_message.present? 13 | %pre= tip.commit_message 14 | - else 15 | = render_markdown tip.comment 16 | -------------------------------------------------------------------------------- /app/views/distributions/_tip_form.html.haml: -------------------------------------------------------------------------------- 1 | - index = "#{(Time.now.to_f * 1000000).round}#{SecureRandom.random_number(1_000_000).to_s.rjust(6, '0')}" 2 | = form.fields_for :tips, tip, child_index: index do |fields| 3 | - user = tip.user 4 | - raise "An user is required" unless user 5 | %tr 6 | %td.recipient 7 | = fields.hidden_field :id unless tip.new_record? 8 | - if user.new_record? 9 | = fields.fields_for :user do |user_fields| 10 | = user_fields.hidden_field :email 11 | = user_fields.hidden_field :nickname 12 | = user.recipient_label 13 | - else 14 | = fields.hidden_field :user_id 15 | = link_to user.recipient_label, user 16 | %td.reason 17 | - if tip.reason 18 | = fields.hidden_field :reason_type 19 | = fields.hidden_field :reason_id 20 | = render "reason", tip: tip 21 | - else 22 | = fields.text_area :comment, hide_label: true, rows: 2 23 | %td.amount= fields.text_field :coin_amount, hide_label: true, append: "PPC" 24 | %td.remove= fields.check_box :_destroy 25 | -------------------------------------------------------------------------------- /app/views/distributions/edit.html.haml: -------------------------------------------------------------------------------- 1 | = render "form" 2 | -------------------------------------------------------------------------------- /app/views/distributions/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1 Distributions 2 | - if @project 3 | %h2= @project.name 4 | %p 5 | %table.table 6 | %thead 7 | %tr 8 | %th Created At 9 | %th Sent at 10 | %th.text-right Amount 11 | %th Transaction 12 | %th Result 13 | %th 14 | %tbody 15 | - @distributions.each do |distribution| 16 | %tr 17 | %td= l distribution.created_at 18 | %td= l distribution.sent_at if distribution.sent_at 19 | %td.text-right= btc_human distribution.total_amount 20 | %td= link_to truncate(distribution.txid), transaction_url(distribution.txid), target: '_blank' if distribution.txid.present? 21 | %td= distribution.is_error ? "Error" : "Success" if distribution.sent? 22 | %td= link_to "Details", [distribution.project, distribution], class: "btn btn-success" 23 | -------------------------------------------------------------------------------- /app/views/distributions/new.html.haml: -------------------------------------------------------------------------------- 1 | = render "form" 2 | -------------------------------------------------------------------------------- /app/views/distributions/new_recipient_form.html.haml: -------------------------------------------------------------------------------- 1 | - output = nil 2 | - bootstrap_form_for Distribution.new do |f| 3 | - output = capture_haml do 4 | - @tips.each do |tip| 5 | = render "tip_form", form: f, tip: tip 6 | = output 7 | -------------------------------------------------------------------------------- /app/views/distributions/show.html.haml: -------------------------------------------------------------------------------- 1 | #distribution-show-page 2 | - total = @distribution.tips.map(&:amount).sum if @distribution.tips.all?(&:amount) 3 | %table.table 4 | %thead 5 | %tr 6 | %th Recipient 7 | %th Reason 8 | %th Address 9 | %th Amount 10 | %th Percentage 11 | %tbody 12 | - @distribution.tips.each do |tip| 13 | %tr 14 | %td.recipient 15 | - if tip.user 16 | - if tip.user.new_record? 17 | = tip.user.recipient_label 18 | - else 19 | = link_to tip.user.recipient_label, tip.user 20 | - else 21 | Nobody 22 | %td.reason= render "reason", tip: tip 23 | %td.address 24 | - if tip.user.try(:bitcoin_address).present? 25 | = tip.user.bitcoin_address 26 | %td.amount 27 | - if tip.amount 28 | = btc_human tip.amount 29 | - else 30 | %em Undecided 31 | %td.percentage= number_to_percentage(tip.amount.to_f * 100 / total, precision: 1) if total and tip.amount and total > 0 32 | 33 | - if total 34 | %p 35 | %strong 36 | Total amount: #{btc_human total} 37 | 38 | - if @distribution.is_error? 39 | %p.alert.alert-danger 40 | The transaction failed. 41 | - elsif @distribution.sent? 42 | %p.alert.alert-success 43 | Transaction sent 44 | - if @distribution.sent_at 45 | on #{l(@distribution.sent_at)} 46 | - elsif !@distribution.all_addresses_known? 47 | %p.alert.alert-warning 48 | The transaction cannot be sent because some addresses are missing. Ask the recipients to sign in and provide an address. 49 | .distribution-actions 50 | - if can? :update, @distribution 51 | .distribution-action 52 | = link_to "Edit the distribution", edit_project_distribution_path(@project, @distribution), class: "btn btn-default" 53 | - if total and can? :send_transaction, @distribution 54 | .distribution-action 55 | = button_to "Send the transaction", send_transaction_project_distribution_path(@project, @distribution), class: "btn btn-danger", data: {confirm: "#{total.to_f / COIN} peercoins will be sent. Are you sure?"} 56 | 57 | %hr 58 | = commontator_thread(@distribution) 59 | -------------------------------------------------------------------------------- /app/views/home/index.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :main_content do 2 | #home-page 3 | / Jumbotron 4 | .jumbotron 5 | .container 6 | %h1 Make anything happen 7 | %p.lead 8 | 9 | %a.btn.btn-lg.btn-success{href: projects_path} See projects 10 | 11 | .container.container-md-height 12 | / Example row of columns 13 | .row.row-md-height 14 | .col-lg-3.col-md-height.col-top.main-panel.donate 15 | .panel-content 16 | %h2 Donate 17 | %p 18 | Donate to projects to make them happen. 19 | .button-container 20 | %a.btn.btn-primary.btn-block{href: projects_path} See projects 21 | .col-lg-3.col-md-height.col-top.main-panel.contribute 22 | .panel-content 23 | %h2 Contribute 24 | %p 25 | Get paid to contribute to a project. 26 | .button-container 27 | %a.btn.btn-primary.btn-block{href: projects_path} See projects 28 | .col-lg-3.col-md-height.col-top.main-panel.raise 29 | .panel-content 30 | %h2 Raise funds 31 | %p 32 | Make something happen by raising funds and distributing them. 33 | .button-container 34 | %a.btn.btn-primary.btn-block{href: new_project_path} Create a project 35 | .col-lg-3.col-md-height.col-top.main-panel.how-it-works 36 | .panel-content 37 | %h2 How it works? 38 | %p 39 | Fundraisers collect funds and distribute them to contributors. 40 | .button-container 41 | %a.btn.btn-primary.btn-block{href: faq_path} FAQ 42 | -------------------------------------------------------------------------------- /app/views/kaminari/_first_page.html.haml: -------------------------------------------------------------------------------- 1 | %li 2 | = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_gap.html.haml: -------------------------------------------------------------------------------- 1 | %li.disabled 2 | = link_to raw(t 'views.pagination.truncate'), '#' 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_last_page.html.haml: -------------------------------------------------------------------------------- 1 | %li 2 | = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_next_page.html.haml: -------------------------------------------------------------------------------- 1 | %li 2 | = link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_page.html.haml: -------------------------------------------------------------------------------- 1 | %li{class: "#{'active' if page.current?}"} 2 | = link_to page, page.current? ? '#' : url, {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil} 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_paginator.html.haml: -------------------------------------------------------------------------------- 1 | = paginator.render do 2 | %ul.pagination 3 | = first_page_tag unless current_page.first? 4 | = prev_page_tag unless current_page.first? 5 | - each_page do |page| 6 | - if page.left_outer? || page.right_outer? || page.inside_window? 7 | = page_tag page 8 | - elsif !page.was_truncated? 9 | = gap_tag 10 | = next_page_tag unless current_page.last? 11 | = last_page_tag unless current_page.last? -------------------------------------------------------------------------------- /app/views/kaminari/_prev_page.html.haml: -------------------------------------------------------------------------------- 1 | %li 2 | = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote 3 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html{lang: "en"} 3 | %head 4 | %meta{charset: "utf-8"}/ 5 | %meta{content: "width=device-width, initial-scale=1.0", name: "viewport"}/ 6 | %meta{content: "", name: "description"}/ 7 | %meta{content: "", name: "author"}/ 8 | %link{href: image_path("ppcoin.png"), rel: "shortcut icon"}/ 9 | 10 | %title= content_for?(:title) ? yield(:title) : "Peer4commit" 11 | 12 | %meta{name: 'description', content: (content_for?(:title) ? yield(:title) : "Donate peercoins to open source projects or make commits and get tips for it.")} 13 | %meta{name: 'keywords', content: 'open source,contribute,github,community,git,bitcoin,peercoin,ppc,tips,perks'} 14 | / %meta{:property => 'og:image', :content => asset_path('logo.png')} 15 | / %link{:rel => 'image_src', :type => 'image/png', :href => asset_path('logo.png')} 16 | 17 | = stylesheet_link_tag "application", media: "all", data: { "turbolinks-track" => true } 18 | = javascript_include_tag "application", data: { "turbolinks-track" => true } 19 | 20 | /[if lt IE 9] 21 | %script{:src => "https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"} 22 | %script{:src => "https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"} 23 | 24 | 25 | 26 | = csrf_meta_tags 27 | %body{data: {environment: Rails.env}} 28 | - if Rails.env.production? 29 | :javascript 30 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 31 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 32 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 33 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 34 | 35 | ga('create', 'UA-11108334-6', 'peer4commit.com'); 36 | ga('send', 'pageview'); 37 | #top-bar 38 | .container 39 | %a#main-logo{href: root_path} 40 | %h3 Peer4commit 41 | = render_flash_message 42 | #main-content 43 | - if content_for?(:main_content) 44 | = yield :main_content 45 | - else 46 | .container#main-container 47 | = yield 48 | #footer 49 | .container 50 | / Site footer 51 | .footer 52 | %p 53 | © 54 | = link_to 'Peer4commit', 'http://peer4commit.com/', target: '_blank' 55 | 2014-2019. Source code is available at #{link_to('github', 'https://github.com/sigmike/peer4commit', target: '_blank')}, 56 | based on #{link_to "Tip4commit", "http://tip4commit.com/"}. 57 | / /container 58 | / 59 | Bootstrap core JavaScript 60 | \================================================== 61 | / Placed at the end of the document so the pages load faster\ 62 | -------------------------------------------------------------------------------- /app/views/layouts/closed.html.haml: -------------------------------------------------------------------------------- 1 | %h1 Closed 2 | 3 | %p 4 | Peer4commit is now closed. All the remaining funds have been sent to #{link_to "the Peercoin Foundation", "https://peercoin.net/foundation.html"}. 5 | See #{link_to "this post", "https://talk.peercoin.net/t/notice-the-peercoin-team-will-be-closing-peer4commit-please-withdraw-all-coins-before-sept-21st/9759"} for more information and discussion. 6 | -------------------------------------------------------------------------------- /app/views/projects/_form.html.haml: -------------------------------------------------------------------------------- 1 | = bootstrap_form_for @project, layout: :horizontal do |f| 2 | = f.alert_message "Please fix the errors below." 3 | = f.text_field :name 4 | = f.text_field :description 5 | = f.text_area :detailed_description, rows: 10 6 | = f.fields_for :tipping_policies_text, @project.tipping_policies_text || @project.build_tipping_policies_text do |fields| 7 | = fields.text_area :text, rows: 10, label: "Tipping policies" 8 | = f.text_field :full_name, label: "GitHub URL (optional)" 9 | = f.form_group do 10 | = f.check_box :auto_tip_commits, {label: "Automatically send 1% of the balance to each commit added to the default branch of the GitHub project. It only works if the user has registered an account on this site."} 11 | = f.form_group do 12 | = f.primary "Save" 13 | -------------------------------------------------------------------------------- /app/views/projects/decide_tip_amounts.html.haml: -------------------------------------------------------------------------------- 1 | %h1 Decide tip amounts 2 | %p 3 | Project balance: #{btc_human @project.available_amount} 4 | = bootstrap_form_for @project, url: decide_tip_amounts_project_path(@project) do |f| 5 | %table.table.table-hover.decide-tip-amounts-table 6 | %thead 7 | %tr 8 | %th Commit 9 | %th Author 10 | %th Message 11 | %th Percentage of balance 12 | %th Free amount 13 | %tbody 14 | = f.fields_for(:tips, @project.tips.select(&:was_undecided?)) do |tip_fields| 15 | = tip_fields.hidden_field :id 16 | - tip = tip_fields.object 17 | - next unless tip.user 18 | %tr 19 | %td= link_to commit_tag(tip.commit), tip.commit_url 20 | %td= tip.user.nickname 21 | %td= simple_format tip.commit_message 22 | %td.col-sm-2 23 | - options = [["Free: 0%", "0"], ["Tiny: 0.1%", "0.1"], ["Small: 0.5%", "0.5"], ["Normal: 1%", "1"], ["Big: 2%", "2"], ["Huge: 5%", "5"]] 24 | = tip_fields.select :decided_amount_percentage, options, hide_label: true, include_blank: true 25 | %td.col-sm-2 26 | = tip_fields.text_field :decided_free_amount, inline: true, hide_label: true, append: "PPC" 27 | 28 | .text-center 29 | = f.submit 'Send the selected tip amounts' 30 | -------------------------------------------------------------------------------- /app/views/projects/donate.html.haml: -------------------------------------------------------------------------------- 1 | .row 2 | .col-md-12 3 | %h1 Donate to #{@project.name} 4 | - fundraisers = @project.users 5 | - if fundraisers.any? 6 | %p 7 | - if fundraisers.size > 1 8 | Before you donate make sure the project collaborators #{fundraisers.map { |user| link_to user.full_name, user }.join(", ").html_safe} are trustworthy and able to achieve what they promised. 9 | - else 10 | - user = fundraisers.first 11 | Before you donate make sure the fundraiser #{link_to user.full_name, user} is trustworthy and able to achieve what he promised. 12 | 13 | - if @donation_address.persisted? and @donation_address.donation_address.present? 14 | %p 15 | To donate to #{link_to @project.name, @project} send Peercoins to this address: #{@donation_address.donation_address} 16 | - else 17 | To donate to this project you can provide a return address. This address will be used if Peer4Commit or the fundraiser ever need to send funds back to you. In the future, this address may also be used to cast a vote. For a vote to be valid, it will need to be signed by the address that the donation was sent from. 18 | 19 | = bootstrap_form_for @donation_address, url: donate_project_path(@project) do |f| 20 | = f.text_field :sender_address, label: 'Return address' 21 | = f.submit "Generate my donation address" 22 | 23 | 24 | %hr 25 | %p If you want to donate without providing a return address, you can just send Peercoins to this address: #{@project.bitcoin_address} 26 | -------------------------------------------------------------------------------- /app/views/projects/donors.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :title do 2 | = "#{@project.name} - Donor list" 3 | 4 | .row 5 | .col-md-12 6 | %h1 7 | = content_for(:title) 8 | %table.table.table-hover.donor-list 9 | %thead 10 | %tr 11 | %th.date Date 12 | %th.amount Amount 13 | %th.sender-address Sender address 14 | %th.transactions Transaction 15 | %tbody 16 | - @project.deposits.includes(:donation_address).order(created_at: :desc).each do |deposit| 17 | %tr.donor-row 18 | %td.date= l(deposit.created_at) 19 | %td.amount= btc_human deposit.amount 20 | %td.sender-address= deposit.donation_address.try(:sender_address).presence || 'No address provided' 21 | %td.transactions.txid 22 | = link_to transaction_url(deposit.txid) do 23 | %abbr{title: deposit.txid}= truncate(deposit.txid, length: 10) 24 | -------------------------------------------------------------------------------- /app/views/projects/edit.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :title do 2 | = @project.name 3 | 4 | %h1= @project.name 5 | .row 6 | .col-md-12 7 | = render "form" 8 | -------------------------------------------------------------------------------- /app/views/projects/index.html.haml: -------------------------------------------------------------------------------- 1 | #project-index 2 | .row 3 | .col-md-10 4 | %h1 Projects 5 | .col-md-2 6 | .text-right 7 | = link_to "Create a project", new_project_path, class: 'btn btn-default btn-block' 8 | %p 9 | %table.table.table-hover 10 | %thead 11 | %tr 12 | %th.name Name 13 | %th.description Description 14 | %th.amount Funds 15 | %th.actions 16 | %tbody 17 | - @projects.each do |project| 18 | %tr 19 | %td.name 20 | %strong= link_to project.name, project 21 | %td.description= project.description 22 | %td.amount= btc_human project.available_amount_cache 23 | %td.actions= link_to 'Details', project, class: 'btn btn-default btn-sm' 24 | = paginate @projects 25 | -------------------------------------------------------------------------------- /app/views/projects/new.html.haml: -------------------------------------------------------------------------------- 1 | - content_for :title do 2 | New project 3 | 4 | %h1 New project 5 | .row 6 | .col-md-12 7 | = render "form" 8 | -------------------------------------------------------------------------------- /app/views/tips/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1 2 | - if @project 3 | = link_to @project.full_name, @project 4 | tips 5 | - else 6 | Tips 7 | %p 8 | %table.table 9 | %thead 10 | %tr 11 | %th Created At 12 | %th Commiter 13 | - unless @project 14 | %th Project 15 | %th Commit 16 | %th Amount 17 | %th Withdrawal 18 | %tbody 19 | - @tips.each do |tip| 20 | %tr 21 | %td= l tip.created_at, format: :short 22 | %td 23 | - if tip.user 24 | - if tip.user.nickname.blank? 25 | = tip.user.full_name 26 | - else 27 | = link_to tip.user.full_name, "https://github.com/#{tip.user.nickname}", target: '_blank' 28 | - unless @project 29 | %td= link_to tip.project.full_name, tip.project 30 | %td 31 | - if tip.commit.present? 32 | = link_to tip.commit[0..6], "https://github.com/#{tip.project.full_name}/commit/#{tip.commit}", target: :blank 33 | %td= btc_human tip.amount 34 | %td{class: tip.distribution.try(:is_error?) ? "danger" : nil} 35 | - if tip.distribution.nil? 36 | - if tip.refunded_at 37 | Refunded to project's deposit 38 | - elsif tip.undecided? 39 | The amount of the tip has not been decided yet 40 | - elsif tip.free? 41 | - elsif tip.user and tip.user.bitcoin_address.blank? 42 | User didn't specify withdrawal address 43 | - elsif tip.project.amount_to_pay < CONFIG["min_payout"].to_d * COIN 44 | The amount of tips for this project is below withdrawal threshold 45 | - else 46 | Waiting for withdrawal 47 | - else 48 | - if tip.distribution.is_error? 49 | Transaction failed 50 | - if tip.distribution.txid.present? 51 | = link_to tip.distribution.txid, transaction_url(tip.distribution.txid), target: :blank 52 | = paginate @tips 53 | -------------------------------------------------------------------------------- /app/views/user_mailer/address_request.html.haml: -------------------------------------------------------------------------------- 1 | %h4 Hello, 2 | 3 | %p 4 | #{@collaborator.nickname} wants to reward you #{@tip.amount ? btc_human(@tip.amount) : ""} on his project #{@tip.project.name}. 5 | = link_to "More details on the distribution", project_distribution_url(@tip.project, @tip.distribution) 6 | 7 | %p 8 | To get your reward you must provide a Peercoin address. 9 | 10 | - if @user.confirmed? 11 | %p= link_to "Set your Peercoin address", edit_registration_url(@user) 12 | - else 13 | %p= link_to 'Set your password and Peercoin address', set_password_and_address_user_url(@user, token: @user.confirmation_token) 14 | 15 | %p Thank you. 16 | 17 | %p= link_to "peer4commit.com", "http://peer4commit.com/" 18 | 19 | %p 20 | %small 21 | = link_to "Don't notify me anymore.", login_users_url(token: @user.login_token, unsubscribe: true) 22 | 23 | -------------------------------------------------------------------------------- /app/views/user_mailer/new_tip.html.haml: -------------------------------------------------------------------------------- 1 | %h4 Hello, #{@user.full_name}! 2 | 3 | %p You were tipped #{btc_human @tip.amount} for your commit on Project #{@tip.project.full_name}. Please, log in and tell us your peercoin address to get it. 4 | 5 | %p Your current balance is #{btc_human @user.balance}. If you don't enter a peercoin address your tips will be returned to the project in 30 days. 6 | 7 | %p= link_to 'Set your password and Peercoin address', set_password_and_address_user_url(@user, token: @user.confirmation_token) 8 | 9 | %p Thanks for contributing to Open Source! 10 | 11 | %p= link_to "peer4commit.com", "http://peer4commit.com/" 12 | 13 | %p 14 | %small 15 | = link_to "Don't notify me anymore, I don't need tips.", login_users_url(token: @user.login_token, unsubscribe: true) 16 | -------------------------------------------------------------------------------- /app/views/user_mailer/security_issue.html.haml: -------------------------------------------------------------------------------- 1 | %h4 Hello #{@user.full_name}, 2 | 3 | %p We recently discovered a security issue on Peer4commit. This issue allowed someone to change the Peercoin address of other users. 4 | 5 | %p 6 | The problem is now fixed. To ensure our database is clean we decided to clear all the addresses. 7 | Please set your Peercoin address again: 8 | = link_to('Sign in', login_users_url(token: @user.login_token)) + "." 9 | 10 | %p We think only one tip was stolen. It will be sent again to its owner when he sets his address. 11 | 12 | %p Sorry for this inconvenience. 13 | 14 | %p= link_to "peer4commit.com", "http://peer4commit.com/" 15 | 16 | %p 17 | %small 18 | = link_to "Don't notify me anymore.", login_users_url(token: @user.login_token, unsubscribe: true) 19 | -------------------------------------------------------------------------------- /app/views/users/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1 Top Contributors 2 | %p 3 | %table.table 4 | %thead 5 | %tr 6 | %th Name 7 | %th Commits tipped 8 | %th Withdrawn 9 | %tbody 10 | - @users.each do |user| 11 | %tr 12 | %td 13 | - if user.nickname.blank? 14 | = user.full_name 15 | - else 16 | = link_to user.full_name, "https://github.com/#{user.nickname}", target: '_blank' 17 | %td= user.commits_count 18 | %td= btc_human user.withdrawn_amount 19 | = paginate @users 20 | -------------------------------------------------------------------------------- /app/views/users/set_password_and_address.html.haml: -------------------------------------------------------------------------------- 1 | = bootstrap_form_for @user, url: request.url do |f| 2 | = f.password_field :password 3 | = f.password_field :password_confirmation 4 | = f.text_field :bitcoin_address 5 | = f.submit "Save" 6 | -------------------------------------------------------------------------------- /app/views/users/show.html.haml: -------------------------------------------------------------------------------- 1 | %h1= @user.name 2 | %p 3 | %strong Identifier: 4 | = @user.identifier 5 | 6 | - if @user.nickname.present? 7 | %p 8 | %strong GitHub account: 9 | = link_to @user.nickname, "https://github.com/#{@user.nickname}" 10 | 11 | - if (projects = @user.projects).any? 12 | %h2 Projects 13 | %ul 14 | - projects.each do |project| 15 | %li= link_to project.to_label, project 16 | 17 | %hr 18 | = commontator_thread(@user) 19 | -------------------------------------------------------------------------------- /app/views/users/update.html.haml: -------------------------------------------------------------------------------- 1 | %h1 Users#update 2 | %p Find me in app/views/users/update.html.haml -------------------------------------------------------------------------------- /bin/airbrake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'airbrake' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('airbrake', 'airbrake') 17 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/bundler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'bundler' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('bundler', 'bundler') 17 | -------------------------------------------------------------------------------- /bin/cap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'cap' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('capistrano', 'cap') 17 | -------------------------------------------------------------------------------- /bin/cdiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'cdiff' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('term-ansicolor', 'cdiff') 17 | -------------------------------------------------------------------------------- /bin/colortab: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'colortab' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('term-ansicolor', 'colortab') 17 | -------------------------------------------------------------------------------- /bin/decolor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'decolor' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('term-ansicolor', 'decolor') 17 | -------------------------------------------------------------------------------- /bin/erubis: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'erubis' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('erubis', 'erubis') 17 | -------------------------------------------------------------------------------- /bin/haml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'haml' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('haml', 'haml') 17 | -------------------------------------------------------------------------------- /bin/lessc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'lessc' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('less', 'lessc') 17 | -------------------------------------------------------------------------------- /bin/rackup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'rackup' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('rack', 'rackup') 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/rdoc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'rdoc' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('rdoc', 'rdoc') 17 | -------------------------------------------------------------------------------- /bin/ri: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'ri' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('rdoc', 'ri') 17 | -------------------------------------------------------------------------------- /bin/sass: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'sass' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('sass', 'sass') 17 | -------------------------------------------------------------------------------- /bin/sass-convert: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'sass-convert' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('sass', 'sass-convert') 17 | -------------------------------------------------------------------------------- /bin/scss: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'scss' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('sass', 'scss') 17 | -------------------------------------------------------------------------------- /bin/sdoc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'sdoc' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('sdoc', 'sdoc') 17 | -------------------------------------------------------------------------------- /bin/sdoc-merge: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'sdoc-merge' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('sdoc', 'sdoc-merge') 17 | -------------------------------------------------------------------------------- /bin/slimrb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'slimrb' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('slim', 'slimrb') 17 | -------------------------------------------------------------------------------- /bin/sprockets: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'sprockets' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('sprockets', 'sprockets') 17 | -------------------------------------------------------------------------------- /bin/term_display: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'term_display' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('term-ansicolor', 'term_display') 17 | -------------------------------------------------------------------------------- /bin/term_mandel: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'term_mandel' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('term-ansicolor', 'term_mandel') 17 | -------------------------------------------------------------------------------- /bin/thor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'thor' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('thor', 'thor') 17 | -------------------------------------------------------------------------------- /bin/tilt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'tilt' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('tilt', 'tilt') 17 | -------------------------------------------------------------------------------- /bin/tt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'tt' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('treetop', 'tt') 17 | -------------------------------------------------------------------------------- /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 | 5 | if host = CONFIG["canonical_host"] 6 | use Rack::CanonicalHost, host 7 | end 8 | 9 | run Rails.application 10 | -------------------------------------------------------------------------------- /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 | CONFIG ||= YAML::load(File.open("config/config.yml")) 10 | 11 | COIN = 1000000 # ppcoin/src/util.h 12 | 13 | module T4c 14 | class Application < Rails::Application 15 | 16 | # Settings in config/environments/* take precedence over those specified here. 17 | # Application configuration should go into files in config/initializers 18 | # -- all .rb files in that directory are automatically loaded. 19 | 20 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 21 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 22 | # config.time_zone = 'Central Time (US & Canada)' 23 | 24 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 25 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 26 | # config.i18n.default_locale = :de 27 | 28 | config.autoload_paths += %W(#{config.root}/lib) 29 | 30 | I18n.enforce_available_locales = false 31 | 32 | config.active_record.raise_in_transactional_callbacks = true 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /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/config.yml.sample: -------------------------------------------------------------------------------- 1 | github: 2 | key: "111111111111" 3 | secret: "111111111111" 4 | 5 | daemon: 6 | username: rpcuser 7 | password: rpcpassword 8 | host: localhost 9 | port: 9904 10 | path: /path/to/ppcoin/src/ppcoind 11 | 12 | devise: 13 | secret: "111111111111" 14 | 15 | application: 16 | secret: "111111111111" 17 | 18 | smtp_settings: 19 | address: smtp.gmail.com 20 | port: 587 21 | domain: foobar.com 22 | user_name: example@foobar.com 23 | password: MY_PASSWORD 24 | authentication: plain 25 | enable_starttls_auto: true 26 | 27 | default_from: contact@example.com 28 | send_all_emails_to: # put an email here if you're testing and you don't want any email sent to others 29 | exception_email: admin@example.com # an email will be sent to this address if an unhandled exception occurs 30 | 31 | # Uncomment to use airbrake/errbit 32 | 33 | # airbrake: 34 | # api_key: 111111111111 35 | # host: errbit.tip4commit.com 36 | 37 | tip: 0.01 38 | min_payout: 1.0 # in PPC 39 | our_fee: 0.05 40 | tipper_delay: "1.hour" 41 | 42 | address_versions: # 55/117 for peercoin, 111/196 for testnet, see base58.h 43 | - 111 44 | - 196 45 | 46 | # canonical_host: peer4commit.example.com # will redirect all other hostnames to this one 47 | -------------------------------------------------------------------------------- /config/cucumber.yml: -------------------------------------------------------------------------------- 1 | <% 2 | rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" 3 | rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}" 4 | std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip" 5 | %> 6 | default: <%= std_opts %> features 7 | wip: --tags @wip:3 --wip features 8 | rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip 9 | -------------------------------------------------------------------------------- /config/database.yml.sample: -------------------------------------------------------------------------------- 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: 5 19 | timeout: 5000 20 | 21 | production: 22 | adapter: mysql2 23 | encoding: utf8 24 | database: peer4commit 25 | username: root 26 | password: 27 | socket: /var/run/mysqld/mysqld.sock 28 | -------------------------------------------------------------------------------- /config/deploy.rb: -------------------------------------------------------------------------------- 1 | set :application, 't4c' 2 | set :repo_url, 'git@github.com:sigmike/peer4commit.git' 3 | 4 | # ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp } 5 | 6 | set :deploy_to, "/home/apps/p4c" 7 | set :scm, :git 8 | 9 | set :rvm_type, :user 10 | set :rvm_ruby_version, '2.0.0-p247' 11 | set :rvm_custom_path, '~/.rvm' 12 | 13 | set :format, :pretty 14 | # set :log_level, :debug 15 | # set :pty, true 16 | 17 | set :linked_files, %w{config/database.yml config/config.yml} 18 | set :linked_dirs, %w{log tmp} 19 | 20 | # set :default_env, { path: "/opt/ruby/bin:$PATH" } 21 | set :keep_releases, 5 22 | 23 | namespace :deploy do 24 | 25 | desc 'Restart application' 26 | task :restart do 27 | on roles(:app), in: :sequence, wait: 5 do 28 | execute :touch, release_path.join('tmp/restart.txt') 29 | end 30 | end 31 | 32 | after :restart, :clear_cache do 33 | on roles(:web), in: :groups, limit: 3, wait: 10 do 34 | # Here we can do anything such as: 35 | # within release_path do 36 | # execute :rake, 'cache:clear' 37 | # end 38 | end 39 | end 40 | 41 | after :finishing, 'deploy:cleanup' 42 | 43 | end 44 | -------------------------------------------------------------------------------- /config/deploy/production.rb: -------------------------------------------------------------------------------- 1 | set :stage, :production 2 | 3 | # Simple Role Syntax 4 | # ================== 5 | # Supports bulk-adding hosts to roles, the primary 6 | # server in each group is considered to be the first 7 | # unless any hosts have the primary property set. 8 | role :app, %w{apps@50.116.2.58} 9 | role :web, %w{apps@50.116.2.58} 10 | role :db, %w{apps@50.116.2.58} 11 | 12 | set :rails_env, 'production' 13 | set :migration_role, 'db' 14 | 15 | # Extended Server Syntax 16 | # ====================== 17 | # This can be used to drop a more detailed server 18 | # definition into the server list. The second argument 19 | # something that quacks like a has can be used to set 20 | # extended properties on the server. 21 | #server 'example.com', user: 'deploy', roles: %w{web app}, my_property: :my_value 22 | 23 | # you can set custom ssh options 24 | # it's possible to pass any option but you need to keep in mind that net/ssh understand limited list of options 25 | # you can see them in [net/ssh documentation](http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start) 26 | # set it globally 27 | # set :ssh_options, { 28 | # keys: %w(/home/rlisowski/.ssh/id_rsa), 29 | # forward_agent: false, 30 | # auth_methods: %w(password) 31 | # } 32 | # and/or per server 33 | # server 'example.com', 34 | # user: 'user_name', 35 | # roles: %w{web app}, 36 | # ssh_options: { 37 | # user: 'user_name', # overrides user setting above 38 | # keys: %w(/home/user_name/.ssh/id_rsa), 39 | # forward_agent: false, 40 | # auth_methods: %w(publickey password) 41 | # # password: 'please use keys' 42 | # } 43 | # setting per server overrides global ssh_options 44 | 45 | # fetch(:default_env).merge!(rails_env: :production) 46 | -------------------------------------------------------------------------------- /config/deploy/staging.rb: -------------------------------------------------------------------------------- 1 | set :stage, :staging 2 | 3 | # Simple Role Syntax 4 | # ================== 5 | # Supports bulk-adding hosts to roles, the primary 6 | # server in each group is considered to be the first 7 | # unless any hosts have the primary property set. 8 | role :app, %w{deploy@example.com} 9 | role :web, %w{deploy@example.com} 10 | role :db, %w{deploy@example.com} 11 | 12 | # Extended Server Syntax 13 | # ====================== 14 | # This can be used to drop a more detailed server 15 | # definition into the server list. The second argument 16 | # something that quacks like a has can be used to set 17 | # extended properties on the server. 18 | server 'example.com', user: 'deploy', roles: %w{web app}, my_property: :my_value 19 | 20 | # you can set custom ssh options 21 | # it's possible to pass any option but you need to keep in mind that net/ssh understand limited list of options 22 | # you can see them in [net/ssh documentation](http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start) 23 | # set it globally 24 | # set :ssh_options, { 25 | # keys: %w(/home/rlisowski/.ssh/id_rsa), 26 | # forward_agent: false, 27 | # auth_methods: %w(password) 28 | # } 29 | # and/or per server 30 | # server 'example.com', 31 | # user: 'user_name', 32 | # roles: %w{web app}, 33 | # ssh_options: { 34 | # user: 'user_name', # overrides user setting above 35 | # keys: %w(/home/user_name/.ssh/id_rsa), 36 | # forward_agent: false, 37 | # auth_methods: %w(publickey password) 38 | # # password: 'please use keys' 39 | # } 40 | # setting per server overrides global ssh_options 41 | 42 | # fetch(:default_env).merge!(rails_env: :staging) 43 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | T4c::Application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | T4c::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | config.action_mailer.default_url_options = { :host => "localhost:3000" } 17 | 18 | config.action_mailer.delivery_method = :smtp 19 | config.action_mailer.smtp_settings = CONFIG['smtp_settings'].to_options 20 | 21 | config.action_mailer.perform_deliveries = true 22 | config.action_mailer.raise_delivery_errors = true 23 | config.action_mailer.default_options = {from: 'no-reply@' + CONFIG['smtp_settings']['domain'] } 24 | 25 | # Print deprecation notices to the Rails logger. 26 | config.active_support.deprecation = :log 27 | 28 | # Raise an error on page load if there are pending migrations 29 | config.active_record.migration_error = :page_load 30 | 31 | # Debug mode disables concatenation and preprocessing of assets. 32 | # This option may cause significant delays in view rendering with a large 33 | # number of complex assets. 34 | config.assets.debug = true 35 | end 36 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | T4c::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static asset server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = "public, max-age=3600" 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | config.action_mailer.default_url_options = {host: "www.example.com"} 35 | config.action_mailer.default_options = {from: 'no-reply@example.com'} 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | end 40 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.action_dispatch.cookies_serializer = :hybrid 2 | -------------------------------------------------------------------------------- /config/initializers/errbit.rb: -------------------------------------------------------------------------------- 1 | if CONFIG['airbrake'] 2 | Airbrake.configure do |config| 3 | config.api_key = CONFIG['airbrake']['api_key'] 4 | config.host = CONFIG['airbrake']['host'] 5 | config.port = 80 6 | config.secure = config.port == 443 7 | end 8 | end -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /config/initializers/intercept_emails.rb: -------------------------------------------------------------------------------- 1 | if (SEND_ALL_EMAILS_TO = CONFIG["send_all_emails_to"]).present? and !Rails.env.test? 2 | class MailInterceptor 3 | def self.delivering_email(message) 4 | message.subject = "[#{CONFIG['smtp_settings']['domain']} to #{message.to.join(", ")}] #{message.subject}" 5 | message.to = SEND_ALL_EMAILS_TO 6 | end 7 | end 8 | 9 | ActionMailer::Base.register_interceptor(MailInterceptor) 10 | end 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/initializers/record_changes.rb: -------------------------------------------------------------------------------- 1 | class ActiveRecord::Base 2 | def self.record_changes(options) 3 | has_many :record_changes, as: :record 4 | 5 | after_save do 6 | state = to_json(options) 7 | last_state = RecordChange.where(record: self).order(created_at: :desc).first.try(:raw_state) 8 | if state != last_state 9 | RecordChange.create!(record: self, raw_state: state) 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used 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 | T4c::Application.config.secret_key_base = CONFIG['application']['secret'] 13 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | T4c::Application.config.session_store :cookie_store, key: '_t4c_session' 4 | -------------------------------------------------------------------------------- /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/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 | edit: "Edit" 14 | titles: 15 | edit: "Edit %{model}" 16 | save: "Save %{model}" 17 | new: "New %{model}" 18 | delete: "Delete %{model}" 19 | -------------------------------------------------------------------------------- /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 | 25 | activerecord: 26 | attributes: 27 | user: 28 | bitcoin_address: Peercoin address 29 | project: 30 | github_url: GitHub URL 31 | detailed_description: Detailed description 32 | tip: 33 | coin_amount: Amount 34 | description: Comment 35 | _destroy: Remove 36 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | T4c::Application.routes.draw do 2 | mount Commontator::Engine => '/commontator' 3 | 4 | root 'home#index' 5 | 6 | get 'audit' => 'home#audit' 7 | get 'faq' => 'home#faq' 8 | 9 | devise_for :users, 10 | controllers: { 11 | omniauth_callbacks: "users/omniauth_callbacks", 12 | registrations: "registrations", 13 | } 14 | 15 | resources :users, :only => [:show, :update, :index] do 16 | collection do 17 | get :login 18 | get :suggestions 19 | end 20 | member do 21 | post :send_tips_back 22 | get :set_password_and_address 23 | patch :set_password_and_address 24 | end 25 | end 26 | resources :projects, :only => [:new, :show, :index, :create, :edit, :update] do 27 | resources :tips, :only => [:index] 28 | resources :distributions, :only => [:new, :create, :show, :index, :edit, :update] do 29 | get :new_recipient_form, on: :collection 30 | post :send_transaction, on: :member 31 | end 32 | member do 33 | get :qrcode 34 | get :decide_tip_amounts 35 | patch :decide_tip_amounts 36 | get :commit_suggestions 37 | get :github_user_suggestions 38 | get :donate 39 | post :donate 40 | get :donors 41 | end 42 | end 43 | resources :tips, :only => [:index] 44 | resources :distributions, :only => [:index] 45 | end 46 | -------------------------------------------------------------------------------- /config/schedule.rb: -------------------------------------------------------------------------------- 1 | # Use this file to easily define all of your cron jobs. 2 | # 3 | # It's helpful, but not entirely necessary to understand cron before proceeding. 4 | # http://en.wikipedia.org/wiki/Cron 5 | 6 | # Example: 7 | # 8 | # set :output, "/path/to/my/cron_log.log" 9 | # 10 | # every 2.hours do 11 | # command "/usr/bin/some_great_command" 12 | # runner "MyModel.some_method" 13 | # rake "some:great:rake:task" 14 | # end 15 | # 16 | # every 4.days do 17 | # runner "AnotherModel.prune_old_records" 18 | # end 19 | 20 | # Learn more: http://github.com/javan/whenever 21 | 22 | require File.expand_path('../../config/environment', __FILE__) 23 | every :reboot do 24 | if daemon = CONFIG['daemon']['path'] 25 | command daemon 26 | end 27 | end 28 | 29 | if delay = CONFIG['tipper_delay'] 30 | delay = eval(delay) 31 | every delay do 32 | runner "BalanceUpdater.work; BitcoinTipper.work; BalanceUpdater.work" 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /db/migrate/20131019133109_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table(:users) do |t| 4 | ## Database authenticatable 5 | t.string :email, :null => false, :default => "" 6 | t.string :encrypted_password, :null => false, :default => "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, :default => 0, :null => false 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.string :current_sign_in_ip 20 | t.string :last_sign_in_ip 21 | 22 | ## Confirmable 23 | # t.string :confirmation_token 24 | # t.datetime :confirmed_at 25 | # t.datetime :confirmation_sent_at 26 | # t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | 34 | t.timestamps 35 | end 36 | 37 | add_index :users, :email, :unique => true 38 | add_index :users, :reset_password_token, :unique => true 39 | # add_index :users, :confirmation_token, :unique => true 40 | # add_index :users, :unlock_token, :unique => true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /db/migrate/20131019133235_add_columns_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddColumnsToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :nickname, :string 4 | add_column :users, :name, :string 5 | add_column :users, :image, :string 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20131019144930_add_bitcoin_address_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddBitcoinAddressToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :bitcoin_address, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131019164745_create_projects.rb: -------------------------------------------------------------------------------- 1 | class CreateProjects < ActiveRecord::Migration 2 | def change 3 | create_table :projects do |t| 4 | t.string :url 5 | t.string :bitcoin_address 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20131019170122_create_deposits.rb: -------------------------------------------------------------------------------- 1 | class CreateDeposits < ActiveRecord::Migration 2 | def change 3 | create_table :deposits do |t| 4 | t.references :project, index: true 5 | t.string :txid 6 | t.integer :confirmations 7 | t.integer :duration, :default => 30.days.to_i 8 | t.integer :paid_out, :limit => 8 9 | t.datetime :paid_out_at 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20131019170659_create_sendmanies.rb: -------------------------------------------------------------------------------- 1 | class CreateSendmanies < ActiveRecord::Migration 2 | def change 3 | create_table :sendmanies do |t| 4 | t.string :txid 5 | t.text :data 6 | t.string :result 7 | t.boolean :is_error 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20131019170925_create_tips.rb: -------------------------------------------------------------------------------- 1 | class CreateTips < ActiveRecord::Migration 2 | def change 3 | create_table :tips do |t| 4 | t.references :deposit, index: true 5 | t.references :user, index: true 6 | t.integer :amount, :limit => 8 7 | t.references :sendmany, index: true 8 | t.boolean :is_refunded 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20131019175751_add_some_fields_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddSomeFieldsToProjects < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :name, :string 4 | add_column :projects, :full_name, :string 5 | add_column :projects, :source_full_name, :string 6 | add_column :projects, :description, :string 7 | add_column :projects, :watchers_count, :integer 8 | add_column :projects, :language, :string 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20131019205025_add_amount_to_deposit.rb: -------------------------------------------------------------------------------- 1 | class AddAmountToDeposit < ActiveRecord::Migration 2 | def change 3 | add_column :deposits, :amount, :integer, :limit => 8 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131019211518_add_last_commit_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddLastCommitToProjects < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :last_commit, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131020003746_add_commit_to_tip.rb: -------------------------------------------------------------------------------- 1 | class AddCommitToTip < ActiveRecord::Migration 2 | def change 3 | add_column :tips, :commit, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131020120722_add_login_token_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddLoginTokenToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :login_token, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131020143021_add_unsubscribed_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddUnsubscribedToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :unsubscribed, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131020145043_add_notified_at_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddNotifiedAtToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :notified_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131030142320_add_project_to_tip.rb: -------------------------------------------------------------------------------- 1 | class AddProjectToTip < ActiveRecord::Migration 2 | def change 3 | add_reference :tips, :project, index: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131030142749_drop_deposit_from_tip.rb: -------------------------------------------------------------------------------- 1 | class DropDepositFromTip < ActiveRecord::Migration 2 | def change 3 | remove_column :tips, :deposit_id 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131030191346_add_available_amount_cache_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddAvailableAmountCacheToProjects < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :available_amount_cache, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131212190037_add_cache_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddCacheToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :commits_count, :integer, default: 0 4 | add_column :users, :withdrawn_amount, :integer, limit: 8, default: 0 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20140102095035_add_refunded_at_to_tips.rb: -------------------------------------------------------------------------------- 1 | class AddRefundedAtToTips < ActiveRecord::Migration 2 | def change 3 | add_column :tips, :refunded_at, :timestamp 4 | remove_column :tips, :is_refunded, :boolean 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20140207061855_add_github_id_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddGithubIdToProjects < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :github_id, :string 4 | end 5 | end -------------------------------------------------------------------------------- /db/migrate/20140209022632_change_projects_description.rb: -------------------------------------------------------------------------------- 1 | class ChangeProjectsDescription < ActiveRecord::Migration 2 | def up 3 | change_column :projects, :description, :text, :limit => nil 4 | end 5 | def down 6 | change_column :projects, :description, :string 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20140209041123_create_indexes_for_projects.rb: -------------------------------------------------------------------------------- 1 | class CreateIndexesForProjects < ActiveRecord::Migration 2 | def change 3 | add_index :projects, :full_name, :unique => true 4 | add_index :projects, :github_id, :unique => true 5 | end 6 | end -------------------------------------------------------------------------------- /db/migrate/20140215062842_add_project_to_sendmany.rb: -------------------------------------------------------------------------------- 1 | class AddProjectToSendmany < ActiveRecord::Migration 2 | def change 3 | add_column :sendmanies, :project_id, :integer 4 | add_index :sendmanies, :project_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20140215094135_add_addres_label_to_project.rb: -------------------------------------------------------------------------------- 1 | class AddAddresLabelToProject < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :address_label, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140215094549_initialize_project_address_label.rb: -------------------------------------------------------------------------------- 1 | class InitializeProjectAddressLabel < ActiveRecord::Migration 2 | def up 3 | execute "UPDATE projects SET address_label=(full_name || '@peer4commit') WHERE address_label IS NULL" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140309161105_change_project_amount_cache_to_big_int.rb: -------------------------------------------------------------------------------- 1 | class ChangeProjectAmountCacheToBigInt < ActiveRecord::Migration 2 | def change 3 | change_column :projects, :available_amount_cache, :integer, limit: 8 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140309192616_create_collaborators.rb: -------------------------------------------------------------------------------- 1 | class CreateCollaborators < ActiveRecord::Migration 2 | def change 3 | create_table :collaborators do |t| 4 | t.belongs_to :project, index: true 5 | t.string :login 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20140323072851_add_hold_tips_to_project.rb: -------------------------------------------------------------------------------- 1 | class AddHoldTipsToProject < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :hold_tips, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140323165816_add_commit_message_to_tip.rb: -------------------------------------------------------------------------------- 1 | class AddCommitMessageToTip < ActiveRecord::Migration 2 | def change 3 | add_column :tips, :commit_message, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140323173320_create_tipping_policies_texts.rb: -------------------------------------------------------------------------------- 1 | class CreateTippingPoliciesTexts < ActiveRecord::Migration 2 | def change 3 | create_table :tipping_policies_texts do |t| 4 | t.belongs_to :project, index: true 5 | t.belongs_to :user, index: true 6 | t.text :text 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20140330165138_create_cold_storage_transfers.rb: -------------------------------------------------------------------------------- 1 | class CreateColdStorageTransfers < ActiveRecord::Migration 2 | def change 3 | create_table :cold_storage_transfers do |t| 4 | t.belongs_to :project, index: true 5 | t.integer :amount, limit: 8 6 | t.string :address 7 | t.string :txid 8 | t.integer :confirmations 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20140401174927_add_cold_storage_withdrawal_address_to_project.rb: -------------------------------------------------------------------------------- 1 | class AddColdStorageWithdrawalAddressToProject < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :cold_storage_withdrawal_address, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140402111051_add_disabled_to_project.rb: -------------------------------------------------------------------------------- 1 | class AddDisabledToProject < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :disabled, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140403062826_add_account_balance_to_project.rb: -------------------------------------------------------------------------------- 1 | class AddAccountBalanceToProject < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :account_balance, :integer, limit: 8 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140405084351_add_disabled_reason_to_project.rb: -------------------------------------------------------------------------------- 1 | class AddDisabledReasonToProject < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :disabled_reason, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140406064344_add_fee_to_sendmany.rb: -------------------------------------------------------------------------------- 1 | class AddFeeToSendmany < ActiveRecord::Migration 2 | def change 3 | add_column :sendmanies, :fee, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140406071705_add_fee_to_cold_storage_transfer.rb: -------------------------------------------------------------------------------- 1 | class AddFeeToColdStorageTransfer < ActiveRecord::Migration 2 | def change 3 | add_column :cold_storage_transfers, :fee, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140529135156_add_detailed_descrition_to_project.rb: -------------------------------------------------------------------------------- 1 | class AddDetailedDescritionToProject < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :detailed_description, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140530132209_rename_sendmany_to_distribution.rb: -------------------------------------------------------------------------------- 1 | class RenameSendmanyToDistribution < ActiveRecord::Migration 2 | def change 3 | rename_table :sendmanies, :distributions 4 | rename_column :tips, :sendmany_id, :distribution_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20140531080839_add_sent_at_to_distribution.rb: -------------------------------------------------------------------------------- 1 | class AddSentAtToDistribution < ActiveRecord::Migration 2 | def change 3 | add_column :distributions, :sent_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140601072522_add_devise_confirmable.rb: -------------------------------------------------------------------------------- 1 | class AddDeviseConfirmable < ActiveRecord::Migration 2 | def change 3 | change_table :users do |t| 4 | t.string :confirmation_token 5 | t.datetime :confirmed_at 6 | t.datetime :confirmation_sent_at 7 | t.string :unconfirmed_email 8 | end 9 | 10 | # Existing users with a GitHub nickname are confirmed 11 | execute "UPDATE users SET confirmed_at='#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}' WHERE nickname IS NOT NULL" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20140601103950_new_default_to_hold_tips.rb: -------------------------------------------------------------------------------- 1 | class NewDefaultToHoldTips < ActiveRecord::Migration 2 | def change 3 | change_column :projects, :hold_tips, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140601104108_remove_unique_constraint_to_project_full_name.rb: -------------------------------------------------------------------------------- 1 | class RemoveUniqueConstraintToProjectFullName < ActiveRecord::Migration 2 | def up 3 | remove_index "projects", "full_name" 4 | end 5 | 6 | def down 7 | add_index "projects", ["full_name"], name: "index_projects_on_full_name", unique: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20140601144116_add_comment_to_tip.rb: -------------------------------------------------------------------------------- 1 | class AddCommentToTip < ActiveRecord::Migration 2 | def change 3 | add_column :tips, :comment, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140601145337_create_versions.rb: -------------------------------------------------------------------------------- 1 | class CreateVersions < ActiveRecord::Migration 2 | def change 3 | create_table :versions do |t| 4 | t.string :item_type, :null => false 5 | t.integer :item_id, :null => false 6 | t.string :event, :null => false 7 | t.string :whodunnit 8 | t.text :object 9 | t.datetime :created_at 10 | end 11 | add_index :versions, [:item_type, :item_id] 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20140601145338_add_object_changes_to_versions.rb: -------------------------------------------------------------------------------- 1 | class AddObjectChangesToVersions < ActiveRecord::Migration 2 | def change 3 | add_column :versions, :object_changes, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140602210025_create_commits.rb: -------------------------------------------------------------------------------- 1 | class CreateCommits < ActiveRecord::Migration 2 | def change 3 | create_table :commits do |t| 4 | t.belongs_to :project, index: true 5 | t.string :sha 6 | t.text :message 7 | t.string :username 8 | t.string :email 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20140607100342_add_origin_to_tip.rb: -------------------------------------------------------------------------------- 1 | class AddOriginToTip < ActiveRecord::Migration 2 | def change 3 | add_reference :tips, :origin, index: true, polymorphic: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140608120038_install_commontator.commontator.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from commontator (originally 0) 2 | class InstallCommontator < ActiveRecord::Migration 3 | def change 4 | create_table 'commontator_comments' do |t| 5 | t.string 'creator_type' 6 | t.integer 'creator_id' 7 | t.string 'editor_type' 8 | t.integer 'editor_id' 9 | t.integer 'thread_id', :null => false 10 | t.text 'body', :null => false 11 | t.datetime 'deleted_at' 12 | 13 | t.integer :cached_votes_up, :default => 0 14 | t.integer :cached_votes_down, :default => 0 15 | 16 | t.timestamps 17 | end 18 | 19 | add_index :commontator_comments, [:creator_id, :creator_type, :thread_id], :name => 'index_commontator_comments_on_c_id_and_c_type_and_t_id' 20 | add_index :commontator_comments, :thread_id 21 | 22 | add_index :commontator_comments, :cached_votes_up 23 | add_index :commontator_comments, :cached_votes_down 24 | 25 | create_table 'commontator_subscriptions' do |t| 26 | t.string 'subscriber_type', :null => false 27 | t.integer 'subscriber_id', :null => false 28 | t.integer 'thread_id', :null => false 29 | 30 | t.timestamps 31 | end 32 | 33 | add_index :commontator_subscriptions, [:subscriber_id, :subscriber_type, :thread_id], :unique => true, :name => 'index_commontator_subscriptions_on_s_id_and_s_type_and_t_id' 34 | add_index :commontator_subscriptions, :thread_id 35 | 36 | create_table 'commontator_threads' do |t| 37 | t.string 'commontable_type' 38 | t.integer 'commontable_id' 39 | t.datetime 'closed_at' 40 | t.string 'closer_type' 41 | t.integer 'closer_id' 42 | 43 | t.timestamps 44 | end 45 | 46 | add_index :commontator_threads, [:commontable_id, :commontable_type], :unique => true, :name => 'index_commontator_threads_on_c_id_and_c_type' 47 | end 48 | end 49 | 50 | -------------------------------------------------------------------------------- /db/migrate/20140608131519_rename_origin_to_reason.rb: -------------------------------------------------------------------------------- 1 | class RenameOriginToReason < ActiveRecord::Migration 2 | def change 3 | rename_column :tips, :origin_type, :reason_type 4 | rename_column :tips, :origin_id, :reason_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20140609122234_drop_version.rb: -------------------------------------------------------------------------------- 1 | class DropVersion < ActiveRecord::Migration 2 | def change 3 | drop_table :versions 4 | end 5 | 6 | def down 7 | create_table "versions", force: true do |t| 8 | t.string "item_type", null: false 9 | t.integer "item_id", null: false 10 | t.string "event", null: false 11 | t.string "whodunnit" 12 | t.text "object" 13 | t.datetime "created_at" 14 | t.text "object_changes" 15 | end 16 | 17 | add_index "versions", ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id" 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20140609122440_create_record_changes.rb: -------------------------------------------------------------------------------- 1 | class CreateRecordChanges < ActiveRecord::Migration 2 | def change 3 | create_table :record_changes do |t| 4 | t.belongs_to :record, index: true, polymorphic: true 5 | t.belongs_to :user 6 | t.text :raw_state, limit: 1.megabyte 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20140615122107_create_donation_addresses.rb: -------------------------------------------------------------------------------- 1 | class CreateDonationAddresses < ActiveRecord::Migration 2 | def change 3 | create_table :donation_addresses do |t| 4 | t.belongs_to :project, index: true 5 | t.string :sender_address 6 | t.string :donation_address 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20140615124857_add_donation_address_to_deposit.rb: -------------------------------------------------------------------------------- 1 | class AddDonationAddressToDeposit < ActiveRecord::Migration 2 | def change 3 | add_reference :deposits, :donation_address, index: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140616055815_add_user_to_collaborator.rb: -------------------------------------------------------------------------------- 1 | class AddUserToCollaborator < ActiveRecord::Migration 2 | def change 3 | add_reference :collaborators, :user, index: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140616060504_convert_collaborator_nick_names_to_user.rb: -------------------------------------------------------------------------------- 1 | class ConvertCollaboratorNickNamesToUser < ActiveRecord::Migration 2 | def up 3 | execute("UPDATE collaborators SET user_id = (SELECT id FROM users WHERE users.nickname = collaborators.login LIMIT 1)") 4 | end 5 | 6 | def down 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20140704060602_add_disabled_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddDisabledToUser < ActiveRecord::Migration 2 | def change 3 | add_column :users, :disabled, :boolean, default: false 4 | add_index :users, :disabled 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20140706075813_remove_git_hub_id_from_project.rb: -------------------------------------------------------------------------------- 1 | class RemoveGitHubIdFromProject < ActiveRecord::Migration 2 | def change 3 | remove_column :projects, :github_id, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140714074128_add_identifier_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddIdentifierToUser < ActiveRecord::Migration 2 | def up 3 | add_column :users, :identifier, :string 4 | execute("SELECT id FROM users WHERE identifier IS NULL").each do |row| 5 | id = row["id"] 6 | charset = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'.split(//) 7 | identifier = (0...12).map { charset.sample }.join 8 | execute "UPDATE users SET identifier='#{identifier}' WHERE id = #{id}" 9 | end 10 | change_column :users, :identifier, :string, null: false 11 | add_index :users, :identifier, unique: true 12 | end 13 | 14 | def down 15 | remove_column :users, :identifier 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20180121184454_add_stake_mint_to_project.rb: -------------------------------------------------------------------------------- 1 | class AddStakeMintToProject < ActiveRecord::Migration 2 | def change 3 | add_column :projects, :stake_mint_amount, :integer, limit: 8 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /features/change_github.feature: -------------------------------------------------------------------------------- 1 | Feature: Fundraiser can change the GitHub repository linked to a project 2 | Scenario: A project not holding tips changes github repository 3 | Given a project 4 | And the project does not hold tips 5 | And the project GitHub name is "foo/bar" 6 | And the commits on GitHub for project "foo/bar" are 7 | | sha | author | email | 8 | | 123 | bob | bobby@example.com | 9 | | abc | alice | alicia@example.com | 10 | | 333 | bob | bobby@example.com | 11 | And our fee is "0" 12 | And a deposit of "500" 13 | Given a GitHub user "bob" who has set his address to "mxWfjaZJTNN5QKeZZYQ5HW3vgALFBsnuG1" 14 | And a GitHub user "alice" who has set his address to "mi9SLroAgc8eUNuLwnZmdyqWdShbNtvr3n" 15 | 16 | When the project tips are built from commits 17 | Then the project should have these tips: 18 | | commit | amount | 19 | | 123 | 5.0 | 20 | | abc | 4.95 | 21 | | 333 | 4.9005 | 22 | 23 | When the project GitHub name is "baz/foo" 24 | And the commits on GitHub for project "baz/foo" are 25 | | sha | author | email | 26 | | aaa | bob | bobby@example.com | 27 | | bbb | alice | alicia@example.com | 28 | | ccc | bob | bobby@example.com | 29 | And the project tips are built from commits 30 | Then the project should have these tips: 31 | | commit | amount | 32 | | 123 | 5.0 | 33 | | abc | 4.95 | 34 | | 333 | 4.9005 | 35 | | aaa | 4.851495 | 36 | | bbb | 4.802981 | 37 | | ccc | 4.754951 | 38 | 39 | -------------------------------------------------------------------------------- /features/cold_storage.feature: -------------------------------------------------------------------------------- 1 | Feature: Some funds are transfered to cold storage 2 | Background: 3 | Given a project 4 | And our fee is "0.01" 5 | And the project address is "mqEtf1CcGtAmoVRHENBVmBRpYppoEcA8LH" 6 | And the project cold storage withdrawal address is "n1g6mxaEpMb6cERcS4bGhmJPjxKc3msvni" 7 | And the cold storage addresses are 8 | | mpjDVmvCgsi2WW9qZJDQN6WgpDTP5iGbpD | 9 | | mr6HkUBp3iUqH6JuvD33banN4vZkifvTGD | 10 | 11 | Scenario: A project receives funds to its non cold storage address 12 | When there's a new incoming transaction of "50" to address "mqEtf1CcGtAmoVRHENBVmBRpYppoEcA8LH" on the project account 13 | And the project balance is updated 14 | Then the project balance should be "49.5" 15 | And the project amount in cold storage should be "0" 16 | 17 | Scenario: A project receives funds to its cold storage address 18 | When there's a new incoming transaction of "50" to address "n1g6mxaEpMb6cERcS4bGhmJPjxKc3msvni" on the project account 19 | And the project balance is updated 20 | Then the project balance should be "0" 21 | And the project amount in cold storage should be "-50" 22 | 23 | Scenario: A project receives funds to an unknown address 24 | When there's a new incoming transaction of "50" to address "mmoS6KKr4Q4v6VQcTQmSWGPgBS8mhJ9f74" on the project account 25 | Then updating the project balance should raise an error 26 | And the project balance should be "0" 27 | And the project amount in cold storage should be "0" 28 | 29 | Scenario: Some funds are sent to cold storage 30 | When there's a new outgoing transaction of "50" to address "mpjDVmvCgsi2WW9qZJDQN6WgpDTP5iGbpD" on the project account 31 | And the project balance is updated 32 | Then the project balance should be "0" 33 | And the project amount in cold storage should be "50" 34 | 35 | Scenario: Unconfirmed transactions are not counted 36 | When there's a new incoming transaction of "50" to address "mqEtf1CcGtAmoVRHENBVmBRpYppoEcA8LH" on the project account with 0 confirmations 37 | And there's a new incoming transaction of "10" to address "n1g6mxaEpMb6cERcS4bGhmJPjxKc3msvni" on the project account with 0 confirmations 38 | And there's a new outgoing transaction of "20" to address "mpjDVmvCgsi2WW9qZJDQN6WgpDTP5iGbpD" on the project account with 0 confirmations 39 | And the project balance is updated 40 | Then the project balance should be "0" 41 | And the project amount in cold storage should be "0" 42 | 43 | Scenario: Sending funds to cold storage 44 | When "50" coins of the project funds are sent to cold storage 45 | Then there should be an outgoing transaction of "50" to address "mpjDVmvCgsi2WW9qZJDQN6WgpDTP5iGbpD" on the project account 46 | 47 | Scenario: Cold storage withdrawal address is created at balance update time if it doesn't exist 48 | Given the project has no cold storage withdrawal address 49 | When the project balance is updated 50 | Then the project should have a cold storage withdrawal address 51 | And the project cold storage withdrawal address should be linked to its account 52 | -------------------------------------------------------------------------------- /features/commit_from_known_nickname.feature: -------------------------------------------------------------------------------- 1 | Feature: A commit with an identified GitHub nickname should be sent to the right user if he exists 2 | Scenario: 3 | Given a project "a" 4 | And our fee is "0" 5 | And a deposit of "500" 6 | And an user "yugo" 7 | And the email of "yugo" is "yugo1@example.com" 8 | And the last known commit is "A" 9 | And a new commit "B" with parent "A" 10 | And the author of commit "B" is "yugo" 11 | And the email of commit "B" is "yugo2@example.com" 12 | 13 | When the new commits are read 14 | Then there should be a tip of "5" for commit "B" 15 | And the tip for commit "B" is for user "yugo" 16 | And there should be no user with email "yugo2@example.com" 17 | -------------------------------------------------------------------------------- /features/distribute_to_user_identifier.feature: -------------------------------------------------------------------------------- 1 | Feature: Distribute funds to an user identifier 2 | 3 | @javascript 4 | Scenario: 5 | Given an user with email "bob@example.com" 6 | And the user with email "bob@example.com" has set his address to "mi9SLroAgc8eUNuLwnZmdyqWdShbNtvr3n" 7 | 8 | Given a project managed by "alice" 9 | And our fee is "0" 10 | And a deposit of "500" 11 | 12 | Given I'm logged in as "alice" 13 | And I go to the project page 14 | And I click on "New distribution" 15 | And I add the user with email "bob@example.com" through his identifier to the recipients 16 | And I fill the amount to "" with "10" 17 | And I save the distribution 18 | 19 | Then I should see these distribution lines: 20 | | recipient | address | amount | percentage | 21 | | | mi9SLroAgc8eUNuLwnZmdyqWdShbNtvr3n | 10 | 100.0 | 22 | 23 | When I click on "Send the transaction" 24 | Then I should see "Transaction sent" 25 | And these amounts should have been sent from the account of the project: 26 | | address | amount | 27 | | mi9SLroAgc8eUNuLwnZmdyqWdShbNtvr3n | 10.0 | 28 | And the project balance should be "490.00" 29 | -------------------------------------------------------------------------------- /features/donate_to_project.feature: -------------------------------------------------------------------------------- 1 | Feature: A visitor can donate to a project 2 | Background: 3 | Given a project 4 | And our fee is "0.01" 5 | 6 | Scenario: A visitor sends coins to a project 7 | When I visit the project page 8 | And I click on "Donate" 9 | And I fill "Return address" with "mmGen7mZTGi9bciEaEa2W1DLsx3HjaFvcd" 10 | And I click on "Generate my donation address" 11 | Then I should see the project donation address associated with "mmGen7mZTGi9bciEaEa2W1DLsx3HjaFvcd" 12 | 13 | Given there's a new incoming transaction of "50" to the donation address associated with "mmGen7mZTGi9bciEaEa2W1DLsx3HjaFvcd" 14 | And the project balance is updated 15 | 16 | When I visit the project page 17 | Then I should see the project balance is "49.5" 18 | 19 | When I click on "List of donors" 20 | Then I should see the donor "mmGen7mZTGi9bciEaEa2W1DLsx3HjaFvcd" sent "50" 21 | 22 | Scenario: Sending twice with the same return address 23 | Given the project has a donation address "mfbDMySWmo4p31waWE4bUGFqK47V4comdq" associated with "mpbkNzunFtBmu3JYENE62UTLtKyvwrSUfx" 24 | When I visit the project page 25 | And I click on "Donate" 26 | And I fill "Return address" with "mpbkNzunFtBmu3JYENE62UTLtKyvwrSUfx" 27 | And I click on "Generate my donation address" 28 | Then I should see "mfbDMySWmo4p31waWE4bUGFqK47V4comdq" 29 | 30 | Scenario: Sending with an invalid return address 31 | When I visit the project page 32 | And I click on "Donate" 33 | And I click on "Generate my donation address" 34 | Then I should see "can't be blank" 35 | When I fill "Return address" with "mpbkNzunFtBmu3JYENE62UTLtKyvwrSUfy" 36 | And I click on "Generate my donation address" 37 | Then I should see "invalid" 38 | 39 | Scenario: Sending without a return address 40 | When I visit the project page 41 | And I click on "Donate" 42 | Then I should see the project donation address 43 | 44 | Given there's a new incoming transaction of "50" to the project donation address 45 | And the project balance is updated 46 | 47 | When I visit the project page 48 | Then I should see the project balance is "49.5" 49 | 50 | When I click on "List of donors" 51 | Then I should see the donor "No address provided" sent "50" 52 | 53 | Scenario: Multiple donations in a single transaction 54 | Given a project "A" with a donation address "mpD1oHHQqAWWrrfxzAg1gEWbHh2teQjYsU" associated with "mpR1otQoiJo8dfXzxyTmvPLg42n7RFJMMh" 55 | And a project "B" with a donation address "mpD2oDRevAtG5Q24jRQx1GKpaZfxM8wh3y" associated with "mpR1otQoiJo8dfXzxyTmvPLg42n7RFJMMh" 56 | And there's a new incoming transaction of "50" to "mpD1oHHQqAWWrrfxzAg1gEWbHh2teQjYsU" in transaction "tx1" 57 | And there's a new incoming transaction of "75" to "mpD2oDRevAtG5Q24jRQx1GKpaZfxM8wh3y" in transaction "tx1" 58 | And the project balances are updated 59 | 60 | When I visit the project "A" page 61 | Then I should see the project balance is "49.50" 62 | 63 | When I click on "List of donors" 64 | Then I should see the donor "mpR1otQoiJo8dfXzxyTmvPLg42n7RFJMMh" sent "50" 65 | 66 | When I visit the project "B" page 67 | Then I should see the project balance is "74.25" 68 | 69 | When I click on "List of donors" 70 | Then I should see the donor "mpR1otQoiJo8dfXzxyTmvPLg42n7RFJMMh" sent "75" 71 | -------------------------------------------------------------------------------- /features/project_detailed_description.feature: -------------------------------------------------------------------------------- 1 | Feature: Project detailed description is markdown formatted 2 | Background: 3 | Given a project 4 | And the project single collaborator is "bob" 5 | And I'm logged in as "bob" 6 | And I go to the project page 7 | And I click on "Edit project" 8 | 9 | Scenario: Standard markdown 10 | When I fill "Detailed description" with: 11 | """ 12 | foo [bar](http://foo.example.com/) 13 | """ 14 | And I click on "Save" 15 | Then I should see a link "bar" to "http://foo.example.com/" 16 | 17 | Scenario: XSS attempt 18 | When I fill "Detailed description" with: 19 | """ 20 | foo [bar](javascript:alert('xss')) 21 | """ 22 | And I click on "Save" 23 | Then I should not see a link "bar" to "javascript:alert('xss')" 24 | 25 | Scenario: Embeded HTML 26 | When I fill "Detailed description" with: 27 | """ 28 | foo bar 29 | """ 30 | And I click on "Save" 31 | Then I should not see a link "bar" to "javascript:alert('xss')" 32 | 33 | Scenario: Inline external image 34 | When I fill "Detailed description" with: 35 | """ 36 | ![foo](http://example.com/img.jpg) 37 | """ 38 | And I click on "Save" 39 | Then I should not see the image "http://example.com/img.jpg" 40 | 41 | -------------------------------------------------------------------------------- /features/step_definitions/cold_storage.rb: -------------------------------------------------------------------------------- 1 | Given(/^the cold storage addresses are$/) do |table| 2 | CONFIG["cold_storage"] ||= {} 3 | CONFIG["cold_storage"]["addresses"] = table.raw.map(&:first) 4 | end 5 | 6 | Given(/^the project address is "(.*?)"$/) do |arg1| 7 | @project.update(bitcoin_address: arg1) 8 | end 9 | 10 | Given(/^the project cold storage withdrawal address is "(.*?)"$/) do |arg1| 11 | @project.update(cold_storage_withdrawal_address: arg1) 12 | end 13 | 14 | When(/^there's a new incoming transaction of "([^"]*?)" on the project account$/) do |arg1| 15 | BitcoinDaemon.instance.add_transaction(account: @project.address_label, amount: arg1.to_d, address: @project.bitcoin_address) 16 | end 17 | 18 | When(/^there's a new incoming transaction of "(.*?)" to address "(.*?)" on the project account$/) do |arg1, arg2| 19 | BitcoinDaemon.instance.add_transaction(account: @project.address_label, amount: arg1.to_d, address: arg2) 20 | end 21 | 22 | When(/^there's a new incoming transaction of "(.*?)" to address "(.*?)" on the project account with (\d+) confirmations$/) do |arg1, arg2, arg3| 23 | BitcoinDaemon.instance.add_transaction(account: @project.address_label, amount: arg1.to_d, address: arg2, confirmations: arg3.to_i) 24 | end 25 | 26 | When(/^there's a new outgoing transaction of "(.*?)" to address "(.*?)" on the project account$/) do |arg1, arg2| 27 | BitcoinDaemon.instance.add_transaction(category: "send", account: @project.address_label, amount: -arg1.to_d, address: arg2) 28 | end 29 | 30 | When(/^there's a new outgoing transaction of "(.*?)" to address "(.*?)" on the project account with (\d+) confirmations$/) do |arg1, arg2, arg3| 31 | BitcoinDaemon.instance.add_transaction(category: "send", account: @project.address_label, amount: -arg1.to_d, address: arg2, confirmations: arg3.to_i) 32 | end 33 | 34 | When(/^the project (?:balance is|balances are) updated$/) do 35 | BalanceUpdater.work 36 | end 37 | 38 | Then(/^updating the project balance should raise an error$/) do 39 | expect { BalanceUpdater.work }.to raise_error(RuntimeError) 40 | end 41 | 42 | Then(/^the project balance should be "(.*?)"$/) do |arg1| 43 | expect(@project.reload.available_amount.to_d / COIN).to eq(arg1.to_d) 44 | end 45 | 46 | Then(/^the project amount in cold storage should be "(.*?)"$/) do |arg1| 47 | expect(@project.reload.cold_storage_amount / COIN).to eq(arg1.to_d) 48 | end 49 | 50 | When(/^"(.*?)" coins of the project funds are sent to cold storage$/) do |arg1| 51 | @project.send_to_cold_storage!((arg1.to_d * COIN).to_i) 52 | end 53 | 54 | Then(/^there should be an outgoing transaction of "(.*?)" to address "(.*?)" on the project account$/) do |arg1, arg2| 55 | transactions = BitcoinDaemon.instance.list_transactions(@project.address_label) 56 | expect(transactions.map { |t| t["category"] }).to eq(["send"]) 57 | expect(transactions.map { |t| t["address"] }).to eq([arg2]) 58 | expect(transactions.map { |t| -t["amount"].to_d / COIN }).to eq([arg1.to_d]) 59 | end 60 | 61 | Given(/^the project has no cold storage withdrawal address$/) do 62 | @project.update(cold_storage_withdrawal_address: nil) 63 | end 64 | 65 | Then(/^the project should have a cold storage withdrawal address$/) do 66 | expect(@project.reload.cold_storage_withdrawal_address).not_to be_blank 67 | end 68 | 69 | Then(/^the project cold storage withdrawal address should be linked to its account$/) do 70 | expect(BitcoinDaemon.instance.get_addresses_by_account(@project.address_label)).to include(@project.reload.cold_storage_withdrawal_address) 71 | end 72 | -------------------------------------------------------------------------------- /features/step_definitions/commit_from_known_nickname.rb: -------------------------------------------------------------------------------- 1 | Given(/^an user "(.*?)"$/) do |arg1| 2 | create(:user, nickname: arg1, email: "#{arg1}@example.com") 3 | end 4 | 5 | Given(/^the email of "(.*?)" is "(.*?)"$/) do |arg1, arg2| 6 | User.find_by_nickname!(arg1).update(email: arg2) 7 | end 8 | 9 | Then(/^the tip for commit "(.*?)" is for user "(.*?)"$/) do |arg1, arg2| 10 | expect(Tip.find_by_commit!(arg1).user.nickname).to eq(arg2) 11 | end 12 | 13 | Then(/^there should be no user with email "(.*?)"$/) do |arg1| 14 | expect(User.where(email: arg1).size).to eq(0) 15 | end 16 | 17 | -------------------------------------------------------------------------------- /features/step_definitions/create_project.rb: -------------------------------------------------------------------------------- 1 | 2 | Then(/^there should be a project "(.*?)"$/) do |arg1| 3 | expect(Project.pluck(:name)).to include(arg1) 4 | @project = Project.where(name: arg1).first 5 | end 6 | 7 | Then(/^the description of the project should be$/) do |string| 8 | expect(@project.description).to eq(string) 9 | end 10 | 11 | Then(/^I should be on the project page$/) do 12 | expect(current_url).to eq(project_url(@project)) 13 | end 14 | 15 | Then(/^there should be no project$/) do 16 | expect(Project.all).to be_empty 17 | end 18 | 19 | Then(/^the GitHub name of the project should be "(.*?)"$/) do |arg1| 20 | expect(@project.full_name).to eq(arg1) 21 | end 22 | 23 | Then(/^the project single collaborators should be "(.*?)"$/) do |arg1| 24 | if arg1 =~ /@/ 25 | expect(@project.collaborators.map(&:user).map(&:email)).to eq([arg1]) 26 | else 27 | expect(@project.collaborators.map(&:user).map(&:nickname)).to eq([arg1]) 28 | end 29 | end 30 | 31 | Then(/^the project address label should be "(.*?)"$/) do |arg1| 32 | expect(@project.address_label).to eq(arg1) 33 | end 34 | 35 | Then(/^the project donation address should be the same as account "(.*?)"$/) do |arg1| 36 | expect(@project.bitcoin_address).to eq(BitcoinDaemon.instance.get_addresses_by_account(arg1).first) 37 | end 38 | 39 | -------------------------------------------------------------------------------- /features/step_definitions/donate_to_project.rb: -------------------------------------------------------------------------------- 1 | Given(/^a project "([^"]*)" with a donation address "([^"]*)" associated with "([^"]*)"$/) do |arg1, arg2, arg3| 2 | @project = Project.create!( 3 | name: arg1, 4 | full_name: "example/#{arg1}", 5 | bitcoin_address: 'mq4NtnmQoQoPfNWEPbhSvxvncgtGo6L8WY', 6 | hold_tips: false, 7 | address_label: "project-#{arg1}", 8 | ) 9 | step %Q[the project has a donation address "#{arg2}" associated with "#{arg3}"] 10 | end 11 | 12 | 13 | Then(/^I should see the project donation address associated with "(.*?)"$/) do |arg1| 14 | address = @project.donation_addresses.find_by(sender_address: arg1).donation_address 15 | expect(address).not_to be_blank 16 | expect(page).to have_content(address) 17 | end 18 | 19 | Given(/^there's a new incoming transaction of "(.*?)" to the donation address associated with "(.*?)"$/) do |arg1, arg2| 20 | address = @project.donation_addresses.find_by(sender_address: arg2).donation_address 21 | expect(address).not_to be_blank 22 | BitcoinDaemon.instance.add_transaction(account: @project.address_label, amount: arg1.to_d, address: address) 23 | end 24 | 25 | Given(/^there's a new incoming transaction of "([^"]*)" to "([^"]*)" in transaction "([^"]*)"$/) do |arg1, arg2, arg3| 26 | donation_address = DonationAddress.find_by!(donation_address: arg2) 27 | project = donation_address.project 28 | BitcoinDaemon.instance.add_transaction( 29 | account: project.address_label, 30 | amount: arg1.to_d, 31 | address: donation_address.donation_address, 32 | txid: arg3, 33 | ) 34 | end 35 | 36 | Then(/^I should see the donor "(.*?)" sent "(.*?)"$/) do |arg1, arg2| 37 | within ".donor-row", text: arg1 do 38 | expect(find(".amount").text.to_d).to eq(arg2.to_d) 39 | end 40 | end 41 | 42 | Given(/^the project has a donation address "(.*?)" associated with "(.*?)"$/) do |arg1, arg2| 43 | @project.donation_addresses.create!(sender_address: arg2, donation_address: arg1) 44 | end 45 | 46 | When(/^there's a new incoming transaction of "([^"]*?)" to the project donation address$/) do |arg1| 47 | BitcoinDaemon.instance.add_transaction(account: @project.address_label, amount: arg1.to_d, address: @project.bitcoin_address) 48 | end 49 | 50 | -------------------------------------------------------------------------------- /features/step_definitions/github.rb: -------------------------------------------------------------------------------- 1 | 2 | Given(/^"(.*?)" is an user registered on GitHub$/) do |arg1| 3 | GITHUB_USERS[arg1] = {} 4 | end 5 | 6 | -------------------------------------------------------------------------------- /features/step_definitions/tip_for_commit.rb: -------------------------------------------------------------------------------- 1 | 2 | Given(/^the project does not hold tips$/) do 3 | @project.update(hold_tips: false) 4 | end 5 | 6 | Given(/^the project GitHub name is "(.*?)"$/) do |arg1| 7 | @project.update(full_name: arg1) 8 | end 9 | 10 | Given(/^the commits on GitHub for project "(.*?)" are$/) do |arg1, table| 11 | @project.reload 12 | expect(@project.full_name).to eq(arg1) 13 | commits = [] 14 | table.hashes.each do |row| 15 | commit = OpenStruct.new( 16 | sha: row["sha"], 17 | author: OpenStruct.new( 18 | login: row["author"], 19 | ), 20 | commit: OpenStruct.new( 21 | message: row["message"] || "Some changes", 22 | author: OpenStruct.new( 23 | email: row["email"] || "author@example.com", 24 | ), 25 | committer: OpenStruct.new( 26 | date: Time.now, 27 | ), 28 | ), 29 | ) 30 | commits << commit 31 | end 32 | 33 | expect(@project).to receive(:get_commits).and_return(commits) 34 | end 35 | 36 | When(/^the project tips are built from commits$/) do 37 | @project.tip_commits 38 | end 39 | 40 | Then(/^the project should have these tips:$/) do |table| 41 | tips = @project.tips.map do |tip| 42 | { 43 | commit: tip.commit, 44 | amount: tip.amount ? (tip.amount.to_f / COIN).to_s : "", 45 | }.with_indifferent_access 46 | end 47 | expect(tips).to eq(table.hashes) 48 | end 49 | -------------------------------------------------------------------------------- /features/step_definitions/tip_modifier_interface.rb: -------------------------------------------------------------------------------- 1 | When(/^I choose the amount "(.*?)" on commit "(.*?)"$/) do |arg1, arg2| 2 | within find(".decide-tip-amounts-table tbody tr", text: arg2) do 3 | select arg1 4 | end 5 | end 6 | 7 | When(/^I fill the free amount with "(.*?)" on commit "(.*?)"$/) do |arg1, arg2| 8 | within find(".decide-tip-amounts-table tbody tr", text: arg2) do 9 | fill_in "Decided free amount", with: arg1 10 | end 11 | end 12 | 13 | When(/^I choose the amount "(.*?)" on all commits$/) do |arg1| 14 | all(".decide-tip-amounts-table tbody tr").each do |tr| 15 | within tr do 16 | select arg1 17 | end 18 | end 19 | end 20 | 21 | When(/^I go to the edit page of the project$/) do 22 | visit edit_project_path(@project) 23 | end 24 | 25 | When(/^I send a forged request to enable tip holding on the project$/) do 26 | page.driver.browser.process_and_follow_redirects(:patch, project_path(@project), project: {hold_tips: "1"}) 27 | end 28 | 29 | Then(/^I should see an access denied$/) do 30 | expect(page).to have_content("Access denied") 31 | end 32 | 33 | Then(/^the project should not hold tips$/) do 34 | expect(@project.reload.hold_tips).to be false 35 | end 36 | 37 | Then(/^the project should hold tips$/) do 38 | expect(@project.reload.hold_tips).to be true 39 | end 40 | 41 | Given(/^the project has undedided tips$/) do 42 | create(:undecided_tip, project: @project) 43 | expect(@project.reload).to have_undecided_tips 44 | end 45 | 46 | Given(/^the project has (\d+) undecided tip$/) do |arg1| 47 | @project.tips.undecided.each(&:destroy) 48 | create(:undecided_tip, project: @project) 49 | expect(@project.reload).to have_undecided_tips 50 | end 51 | 52 | Given(/^I send a forged request to set the amount of the first undecided tip of the project$/) do 53 | tip = @project.tips.undecided.first 54 | expect(tip).not_to be_nil 55 | params = { 56 | project: { 57 | tips_attributes: { 58 | "0" => { 59 | id: tip.id, 60 | decided_amount_percentage: "5", 61 | }, 62 | }, 63 | }, 64 | } 65 | 66 | page.driver.browser.process_and_follow_redirects(:patch, decide_tip_amounts_project_path(@project), params) 67 | end 68 | 69 | When(/^I send a forged request to change the percentage of commit "(.*?)" on project "(.*?)" to "(.*?)"$/) do |arg1, arg2, arg3| 70 | project = find_project(arg2) 71 | tip = project.tips.detect { |t| t.commit == arg1 } 72 | expect(tip).not_to be_nil 73 | params = { 74 | project: { 75 | tips_attributes: { 76 | "0" => { 77 | id: tip.id, 78 | decided_amount_percentage: arg3, 79 | }, 80 | }, 81 | }, 82 | } 83 | 84 | page.driver.browser.process_and_follow_redirects(:patch, decide_tip_amounts_project_path(project), params) 85 | end 86 | 87 | Then(/^the project should have (\d+) undecided tips$/) do |arg1| 88 | expect(@project.tips.undecided.size).to eq(arg1.to_i) 89 | end 90 | 91 | Then(/^there should be (\d+) tip$/) do |arg1| 92 | expect(@project.reload.tips.size).to eq(arg1.to_i) 93 | end 94 | -------------------------------------------------------------------------------- /features/step_definitions/user_identifier.rb: -------------------------------------------------------------------------------- 1 | Then(/^I should see the identifier of "(.*?)"$/) do |arg1| 2 | identifier = User.find_by(email: arg1).identifier 3 | expect(identifier).to be_present 4 | expect(page).to have_content identifier 5 | end 6 | -------------------------------------------------------------------------------- /features/support/big_decimal_inspect.rb: -------------------------------------------------------------------------------- 1 | class BigDecimal 2 | def inspect 3 | "" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /features/support/bitcoin_daemon_mock.rb: -------------------------------------------------------------------------------- 1 | class BitcoinDaemonMock 2 | def initialize 3 | @transactions = [] 4 | @addresses_by_account = Hash.new 5 | end 6 | 7 | def random_address 8 | "random_address" 9 | end 10 | 11 | def add_transaction(options) 12 | transaction = { 13 | "account" => "", 14 | "address" => random_address, 15 | "category" => "receive", 16 | "amount" => 10.0, 17 | "confirmations" => 10, 18 | "blockhash" => SecureRandom.hex(64), 19 | "blockindex" => 3, 20 | "txid" => SecureRandom.hex(64), 21 | "time" => Time.now.to_i, 22 | }.merge(options.stringify_keys) 23 | @transactions << transaction 24 | end 25 | 26 | def list_transactions(account = "", count = 10, from = 0) 27 | @transactions.select { |t| account == "*" ? true : (t["account"] == account) }[from, count] 28 | end 29 | 30 | def clear_transaction_history 31 | @transactions.clear 32 | end 33 | 34 | def send_many(account, recipients, minconf = 1) 35 | txid = SecureRandom.hex(64) 36 | recipients.each do |recipient, amount| 37 | @transactions << { 38 | "account" => account, 39 | "address" => recipient, 40 | "category" => "send", 41 | "amount" => -amount.to_f, 42 | "confirmations" => 10, 43 | "blockhash" => SecureRandom.hex(64), 44 | "blockindex" => 3, 45 | "txid" => txid, 46 | "time" => Time.now.to_i, 47 | } 48 | end 49 | txid 50 | end 51 | 52 | def get_new_address(account) 53 | @addresses_by_account[account] ||= [] 54 | address = SecureRandom.hex(10) 55 | @addresses_by_account[account] << address 56 | address 57 | end 58 | 59 | def get_addresses_by_account(account) 60 | @addresses_by_account[account] || [] 61 | end 62 | 63 | def get_balance(account) 64 | 0 65 | end 66 | end 67 | 68 | Before do 69 | BitcoinDaemon.instance_eval do 70 | @bitcoin_daemon = BitcoinDaemonMock.new 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | 7 | require 'cucumber/rails' 8 | 9 | # Capybara defaults to CSS3 selectors rather than XPath. 10 | # If you'd prefer to use XPath, just uncomment this line and adjust any 11 | # selectors in your step definitions to use the XPath syntax. 12 | # Capybara.default_selector = :xpath 13 | 14 | # By default, any exception happening in your Rails application will bubble up 15 | # to Cucumber so that your scenario will fail. This is a different from how 16 | # your application behaves in the production environment, where an error page will 17 | # be rendered instead. 18 | # 19 | # Sometimes we want to override this default behaviour and allow Rails to rescue 20 | # exceptions and display an error page (just like when the app is running in production). 21 | # Typical scenarios where you want to do this is when you test your error pages. 22 | # There are two ways to allow Rails to rescue exceptions: 23 | # 24 | # 1) Tag your scenario (or feature) with @allow-rescue 25 | # 26 | # 2) Set the value below to true. Beware that doing this globally is not 27 | # recommended as it will mask a lot of errors for you! 28 | # 29 | ActionController::Base.allow_rescue = false 30 | 31 | # Remove/comment out the lines below if your app doesn't have a database. 32 | # For some databases (like MongoDB and CouchDB) you may need to use :truncation instead. 33 | begin 34 | DatabaseCleaner.strategy = :transaction 35 | rescue NameError 36 | raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it." 37 | end 38 | 39 | # You may also want to configure DatabaseCleaner to use different strategies for certain features and scenarios. 40 | # See the DatabaseCleaner documentation for details. Example: 41 | # 42 | # Before('@no-txn,@selenium,@culerity,@celerity,@javascript') do 43 | # # { :except => [:widgets] } may not do what you expect here 44 | # # as Cucumber::Rails::Database.javascript_strategy overrides 45 | # # this setting. 46 | # DatabaseCleaner.strategy = :truncation 47 | # end 48 | # 49 | # Before('~@no-txn', '~@selenium', '~@culerity', '~@celerity', '~@javascript') do 50 | # DatabaseCleaner.strategy = :transaction 51 | # end 52 | # 53 | 54 | # Possible values are :truncation and :transaction 55 | # The :transaction strategy is faster, but might give you threading problems. 56 | # See https://github.com/cucumber/cucumber-rails/blob/master/features/choose_javascript_database_strategy.feature 57 | Cucumber::Rails::Database.javascript_strategy = :truncation 58 | 59 | require 'capybara/poltergeist' 60 | if ENV["FIREFOX"] 61 | Capybara.javascript_driver = :selenium 62 | else 63 | Capybara.register_driver :poltergeist do |app| 64 | Capybara::Poltergeist::Driver.new(app, inspector: true) 65 | end 66 | Capybara.javascript_driver = :poltergeist 67 | end 68 | 69 | require 'capybara-screenshot/cucumber' 70 | -------------------------------------------------------------------------------- /features/support/factory_girl.rb: -------------------------------------------------------------------------------- 1 | World(FactoryGirl::Syntax::Methods) 2 | -------------------------------------------------------------------------------- /features/support/finders.rb: -------------------------------------------------------------------------------- 1 | def find_project(name) 2 | project = Project.where(full_name: "example/#{name}").first 3 | project or raise "Project #{name.inspect} not found" 4 | end 5 | -------------------------------------------------------------------------------- /features/support/octokit_mock.rb: -------------------------------------------------------------------------------- 1 | class Octokit::Client 2 | def initialize(*args) 3 | end 4 | 5 | def commits(*args) 6 | [] 7 | end 8 | 9 | def user(login) 10 | GITHUB_USERS.fetch(login) do 11 | raise Octokit::NotFound 12 | end 13 | end 14 | end 15 | 16 | GITHUB_USERS = {} 17 | 18 | Before do 19 | GITHUB_USERS.clear 20 | end 21 | -------------------------------------------------------------------------------- /features/support/rspec_doubles.rb: -------------------------------------------------------------------------------- 1 | require 'cucumber/rspec/doubles' 2 | -------------------------------------------------------------------------------- /features/support/to_ostruct.rb: -------------------------------------------------------------------------------- 1 | require 'ostruct' 2 | 3 | class Hash 4 | def to_ostruct 5 | o = OpenStruct.new(self) 6 | each do |k,v| 7 | o.send(:"#{k}=", v.to_ostruct) if v.respond_to? :to_ostruct 8 | end 9 | o 10 | end 11 | end 12 | 13 | class Array 14 | def to_ostruct 15 | map do |item| 16 | if item.respond_to? :to_ostruct 17 | item.to_ostruct 18 | else 19 | item 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /features/tip_for_commit.feature: -------------------------------------------------------------------------------- 1 | Feature: On projects not holding tips, a tip is created for each new commit 2 | Scenario: A project not holding tips 3 | Given a project 4 | And the project does not hold tips 5 | And the project GitHub name is "foo/bar" 6 | And the commits on GitHub for project "foo/bar" are 7 | | sha | author | email | 8 | | 123 | bob | bobby@example.com | 9 | | abc | alice | alicia@example.com | 10 | | 333 | bob | bobby@example.com | 11 | And our fee is "0" 12 | And a deposit of "500" 13 | Given a GitHub user "bob" who has set his address to "mxWfjaZJTNN5QKeZZYQ5HW3vgALFBsnuG1" 14 | 15 | When the project tips are built from commits 16 | Then the project should have these tips: 17 | | commit | amount | 18 | | 123 | 5.0 | 19 | | 333 | 4.95 | 20 | 21 | When the tipper is started 22 | Then these amounts should have been sent from the account of the project: 23 | | address | amount | 24 | | mxWfjaZJTNN5QKeZZYQ5HW3vgALFBsnuG1 | 9.95 | 25 | 26 | And no email should have been sent 27 | 28 | Scenario: A project holding tips 29 | Given a project 30 | And the project holds tips 31 | And the project GitHub name is "foo/bar" 32 | And the commits on GitHub for project "foo/bar" are 33 | | sha | author | email | 34 | | 123 | bob | bobby@example.com | 35 | | abc | alice | alicia@example.com | 36 | | 333 | bob | bobby@example.com | 37 | And our fee is "0" 38 | And a deposit of "500" 39 | And a GitHub user "bob" who has set his address to "mxWfjaZJTNN5QKeZZYQ5HW3vgALFBsnuG1" 40 | 41 | When the project tips are built from commits 42 | Then the project should have these tips: 43 | | commit | amount | 44 | | 123 | | 45 | | 333 | | 46 | 47 | When the tipper is started 48 | Then no coins should have been sent 49 | -------------------------------------------------------------------------------- /features/tipping_policies.feature: -------------------------------------------------------------------------------- 1 | Feature: A project collaborator can display the tipping policies of the project 2 | Background: 3 | Given a project 4 | And the project collaborators are: 5 | | seldon | 6 | | daneel | 7 | 8 | Scenario: A collaborator changes the tipping policies 9 | Given I'm logged in as "seldon" 10 | And I go to the project page 11 | And I click on "Edit project" 12 | And I fill "Tipping policies" with: 13 | """ 14 | All commits are huge! 15 | 16 | Blah blah 17 | """ 18 | And I click on "Save" 19 | Then I should see "The project has been updated" 20 | 21 | Given I'm not logged in 22 | And I go to the project page 23 | Then I should see "All commits are huge!" 24 | And I should see "Blah blah" 25 | And I should see "seldon" 26 | -------------------------------------------------------------------------------- /features/user_identifier.feature: -------------------------------------------------------------------------------- 1 | Feature: Each user has an unique identifier 2 | Scenario: New email user gets an unique identifier 3 | When I visit the home page 4 | And I click on "Sign in" 5 | And I click on "Sign up" 6 | And I fill "Email" with "bob@example.com" 7 | And I fill "Password" with "password" 8 | And I fill "Password confirmation" with "password" 9 | And I click on "Sign up" 10 | Then I should see "confirmation link" 11 | 12 | And an email should have been sent to "bob@example.com" 13 | When I click on "Confirm my account" in the email 14 | Then I should see "confirmed" 15 | 16 | And I fill "Email" with "bob@example.com" 17 | And I fill "Password" with "password" 18 | And I click on "Sign in" in the sign in form 19 | When I go to edit my profile 20 | Then I should see the identifier of "bob@example.com" 21 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/lib/assets/.keep -------------------------------------------------------------------------------- /lib/bitcoin_address_validator.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | 3 | class BitcoinAddressValidator < ActiveModel::EachValidator 4 | def validate_each(record, field, value) 5 | unless value.blank? || valid_bitcoin_address?(value) 6 | record.errors[field] << "Peercoin address is invalid" 7 | end 8 | end 9 | 10 | private 11 | 12 | B58Chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 13 | B58Base = B58Chars.length 14 | 15 | def valid_bitcoin_address?(address) 16 | if (address =~ /^[a-zA-Z1-9]{33,35}$/) and version = version(address) 17 | if (expected_versions = CONFIG["address_versions"]).present? 18 | expected_versions.include?(version.ord) 19 | else 20 | true 21 | end 22 | else 23 | false 24 | end 25 | end 26 | 27 | def version(address) 28 | decoded = b58_decode(address, 25) 29 | 30 | version = decoded[0, 1] 31 | checksum = decoded[-4, decoded.length] 32 | vh160 = decoded[0, decoded.length - 4] 33 | 34 | hashed = (Digest::SHA2.new << (Digest::SHA2.new << vh160).digest).digest 35 | 36 | hashed[0, 4] == checksum ? version[0] : nil 37 | end 38 | 39 | def b58_decode(value, length) 40 | long_value = 0 41 | index = 0 42 | result = "" 43 | 44 | value.reverse.each_char do |c| 45 | long_value += B58Chars.index(c) * (B58Base ** index) 46 | index += 1 47 | end 48 | 49 | while long_value >= 256 do 50 | div, mod = long_value.divmod 256 51 | result = mod.chr + result 52 | long_value = div 53 | end 54 | 55 | result = long_value.chr + result 56 | 57 | if result.length < length 58 | result = 0.chr * (length - result.length) + result 59 | end 60 | 61 | result 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/bitcoin_daemon.rb: -------------------------------------------------------------------------------- 1 | class BitcoinDaemon 2 | def self.instance 3 | @bitcoin_daemon ||= BitcoinDaemon.new(CONFIG['daemon']) 4 | end 5 | 6 | class RPCError < StandardError 7 | attr_accessor :code 8 | def initialize(code, message) 9 | @code = code 10 | super(message) 11 | end 12 | end 13 | 14 | attr_reader :config 15 | 16 | def initialize(config) 17 | @config = config || {} 18 | end 19 | 20 | def rpc(command, *params) 21 | %w( username password port host ).each do |field| 22 | raise "No #{field} provided in daemon config" if config[field].blank? 23 | end 24 | 25 | uri = URI::HTTP.build(host: config['host'], port: config['port'].to_i) 26 | 27 | auth = config.slice('username', 'password').symbolize_keys 28 | 29 | data = { 30 | method: command, 31 | params: params, 32 | id: 1, 33 | } 34 | 35 | Rails.logger.info "RPC Command: #{data.inspect}" 36 | response = HTTParty.post(uri.to_s, body: data.to_json, basic_auth: auth) 37 | 38 | result = JSON.parse(response.body) 39 | if error = result["error"] 40 | raise RPCError.new(error["code"], error["message"]) 41 | end 42 | result["result"] 43 | end 44 | 45 | def get_new_address(account = "") 46 | rpc('getnewaddress', account) 47 | end 48 | 49 | def list_transactions(account = "", count = 10, from = 0) 50 | rpc('listtransactions', account, count, from) 51 | end 52 | 53 | def send_many(account, recipients, minconf = 1) 54 | recipients = recipients.dup 55 | recipients.each do |address, amount| 56 | recipients[address] = amount.to_f 57 | end 58 | rpc('sendmany', account, recipients, minconf) 59 | end 60 | 61 | def get_balance(account = "") 62 | rpc('getbalance', account).to_f 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/bitcoin_tipper.rb: -------------------------------------------------------------------------------- 1 | class BitcoinTipper 2 | def self.work_forever 3 | while true do 4 | self.work 5 | end 6 | end 7 | 8 | def self.work 9 | Rails.logger.info "Traversing projects..." 10 | Project.enabled.find_each do |project| 11 | Rails.logger.info " Project #{project.id} #{project.full_name}" 12 | project.update_commits 13 | project.tip_commits 14 | end 15 | 16 | Rails.logger.info "Sending tips to commits..." 17 | Project.enabled.find_each do |project| 18 | tips = project.tips_to_pay 19 | amount = tips.sum(&:amount).to_d 20 | if amount > CONFIG["min_payout"].to_d * COIN 21 | distribution = Distribution.create(project_id: project.id) 22 | tips.each do |tip| 23 | tip.update_attribute :distribution_id, distribution.id 24 | end 25 | distribution.reload.send_transaction! 26 | Rails.logger.info " #{distribution.inspect}" 27 | end 28 | end 29 | 30 | Rails.logger.info "Refunding unclaimed tips..." 31 | Tip.refund_unclaimed 32 | 33 | Rails.logger.info "Updating projects cache..." 34 | Project.update_cache 35 | 36 | Rails.logger.info "Updating users cache..." 37 | User.update_cache 38 | end 39 | 40 | def self.create_distributions 41 | Rails.logger.info "Creating distribution" 42 | ActiveRecord::Base.transaction do 43 | end 44 | end 45 | 46 | end 47 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/cucumber.rake: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | 7 | 8 | unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks 9 | 10 | vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 11 | $LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? 12 | 13 | begin 14 | require 'cucumber/rake/task' 15 | 16 | namespace :cucumber do 17 | Cucumber::Rake::Task.new({:ok => 'test:prepare'}, 'Run features that should pass') do |t| 18 | t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. 19 | t.fork = true # You may get faster startup if you set this to false 20 | t.profile = 'default' 21 | end 22 | 23 | Cucumber::Rake::Task.new({:wip => 'test:prepare'}, 'Run features that are being worked on') do |t| 24 | t.binary = vendored_cucumber_bin 25 | t.fork = true # You may get faster startup if you set this to false 26 | t.profile = 'wip' 27 | end 28 | 29 | Cucumber::Rake::Task.new({:rerun => 'test:prepare'}, 'Record failing features and run only them if any exist') do |t| 30 | t.binary = vendored_cucumber_bin 31 | t.fork = true # You may get faster startup if you set this to false 32 | t.profile = 'rerun' 33 | end 34 | 35 | desc 'Run all features' 36 | task :all => [:ok, :wip] 37 | 38 | task :statsetup do 39 | require 'rails/code_statistics' 40 | ::STATS_DIRECTORIES << %w(Cucumber\ features features) if File.exist?('features') 41 | ::CodeStatistics::TEST_TYPES << "Cucumber features" if File.exist?('features') 42 | end 43 | end 44 | desc 'Alias for cucumber:ok' 45 | task :cucumber => 'cucumber:ok' 46 | 47 | task :default => :cucumber 48 | 49 | task :features => :cucumber do 50 | STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" 51 | end 52 | 53 | # In case we don't have the generic Rails test:prepare hook, append a no-op task that we can depend upon. 54 | task 'test:prepare' do 55 | end 56 | 57 | task :stats => 'cucumber:statsetup' 58 | rescue LoadError 59 | desc 'cucumber rake task not available (cucumber not installed)' 60 | task :cucumber do 61 | abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' 62 | end 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /lib/tasks/reassign_noreply_tips.rake: -------------------------------------------------------------------------------- 1 | task :reassign_noreply_tips => :environment do 2 | logger = Rails.logger 3 | logger.info "Reassigning noreply tips" 4 | 5 | User.transaction do 6 | User.where("email like '%@users.noreply.github.com'").each do |user| 7 | next unless user.nickname.present? 8 | 9 | all = User.where(nickname: user.nickname) 10 | users_with_address = all.select(&:bitcoin_address) 11 | next if users_with_address.size != 1 12 | 13 | real_user = users_with_address.first 14 | logger.info "Real user: #{real_user.inspect}" 15 | 16 | all.each do |other| 17 | next if other == real_user 18 | logger.info "Reassigning tips from user #{other.inspect}" 19 | other.tips.each do |tip| 20 | if tip.project.disabled? 21 | logger.info "Skipping disabled project on tip #{tip.inspect}" 22 | next 23 | end 24 | logger.info "Reassigning tip #{tip.inspect}" 25 | tip.user = real_user 26 | if tip.refunded? 27 | logger.info "Canceling refunded state" 28 | tip.refunded_at = nil 29 | end 30 | if ENV["PROCEED"] == "yes" 31 | tip.save! 32 | end 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/tasks/send_security_issue.rake: -------------------------------------------------------------------------------- 1 | task :send_security_issue => :environment do 2 | User.where(unsubscribed: nil).each do |user| 3 | UserMailer.security_issue(user).deliver 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/templates/haml/scaffold/_form.html.haml: -------------------------------------------------------------------------------- 1 | = simple_form_for(@<%= singular_table_name %>) do |f| 2 | = f.error_notification 3 | 4 | .form-inputs 5 | <%- attributes.each do |attribute| -%> 6 | = f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> 7 | <%- end -%> 8 | 9 | .form-actions 10 | = f.button :submit 11 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/log/.keep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

The page you were looking for doesn't exist.

54 |

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

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

The change you wanted was rejected.

54 |

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

55 |
56 |

If you are the application owner check the logs for more information.

57 | 58 | 59 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 48 | 49 | 50 | 51 | 52 |
53 |

We're sorry, but something went wrong.

54 |
55 |

If you are the application owner check the logs for more information.

56 | 57 | 58 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /users/ 3 | -------------------------------------------------------------------------------- /public/webfonts/28BE49_0_0.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/public/webfonts/28BE49_0_0.eot -------------------------------------------------------------------------------- /public/webfonts/28BE49_0_0.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/public/webfonts/28BE49_0_0.ttf -------------------------------------------------------------------------------- /public/webfonts/28BE49_0_0.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/public/webfonts/28BE49_0_0.woff -------------------------------------------------------------------------------- /script/cucumber: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 4 | if vendored_cucumber_bin 5 | load File.expand_path(vendored_cucumber_bin) 6 | else 7 | require 'rubygems' unless ENV['NO_RUBYGEMS'] 8 | require 'cucumber' 9 | load Cucumber::BINARY 10 | end 11 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/home_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class HomeControllerTest < ActionController::TestCase 4 | test "should get index" do 5 | get :index 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/controllers/projects_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ProjectsControllerTest < ActionController::TestCase 4 | test "should get index" do 5 | get :index 6 | assert_response :success 7 | end 8 | 9 | test "should get show" do 10 | get :show 11 | assert_response :success 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/controllers/users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersControllerTest < ActionController::TestCase 4 | test "should get show" do 5 | get :show 6 | assert_response :success 7 | end 8 | 9 | test "should get update" do 10 | get :update 11 | assert_response :success 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /test/factories/tips.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :tip do 5 | association :user 6 | amount 2 7 | commit { Digest::SHA1.hexdigest(SecureRandom.hex) } 8 | 9 | factory :undecided_tip do 10 | amount nil 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/factories/users.rb: -------------------------------------------------------------------------------- 1 | # Read about factories at https://github.com/thoughtbot/factory_girl 2 | 3 | FactoryGirl.define do 4 | factory :user do 5 | sequence(:email) { |n| "user#{n}@example.com" } 6 | password "password" 7 | confirmed_at { Time.now } 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/commits.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | project_id: 5 | sha: MyString 6 | message: MyText 7 | username: MyString 8 | email: MyString 9 | 10 | two: 11 | project_id: 12 | sha: MyString 13 | message: MyText 14 | username: MyString 15 | email: MyString 16 | -------------------------------------------------------------------------------- /test/fixtures/deposits.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | project_id: 5 | txid: MyString 6 | confirmations: 1 7 | duration: 1 8 | paid_out: 1 9 | paid_out_at: 2013-10-19 23:01:22 10 | 11 | two: 12 | project_id: 13 | txid: MyString 14 | confirmations: 1 15 | duration: 1 16 | paid_out: 1 17 | paid_out_at: 2013-10-19 23:01:22 18 | -------------------------------------------------------------------------------- /test/fixtures/donation_addresses.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | project_id: 5 | sender_address: MyString 6 | donation_address: MyString 7 | 8 | two: 9 | project_id: 10 | sender_address: MyString 11 | donation_address: MyString 12 | -------------------------------------------------------------------------------- /test/fixtures/projects.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | url: MyString 5 | bitcoin_address: MyString 6 | 7 | two: 8 | url: MyString 9 | bitcoin_address: MyString 10 | -------------------------------------------------------------------------------- /test/fixtures/sendmanies.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | txid: MyString 5 | data: MyText 6 | result: MyString 7 | is_error: false 8 | 9 | two: 10 | txid: MyString 11 | data: MyText 12 | result: MyString 13 | is_error: false 14 | -------------------------------------------------------------------------------- /test/fixtures/tipping_policies_texts.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | project_id: 5 | user_id: 6 | text: MyText 7 | 8 | two: 9 | project_id: 10 | user_id: 11 | text: MyText 12 | -------------------------------------------------------------------------------- /test/fixtures/tips.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | user_id: 5 | amount: 1 6 | distribution_id: 7 | refunded_at: false 8 | 9 | two: 10 | user_id: 11 | amount: 1 12 | distribution_id: 13 | refunded_at: false 14 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: {} 8 | # column: value 9 | # 10 | two: {} 11 | # column: value 12 | -------------------------------------------------------------------------------- /test/helpers/home_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class HomeHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/projects_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ProjectsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/sessions_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/users_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/user_mailer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserMailerTest < ActionMailer::TestCase 4 | test "new_tip" do 5 | mail = UserMailer.new_tip 6 | assert_equal "New tip", mail.subject 7 | assert_equal ["to@example.org"], mail.to 8 | assert_equal ["from@example.com"], mail.from 9 | assert_match "Hi", mail.body.encoded 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /test/models/commit_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CommitTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/deposit_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DepositTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/donation_address_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DonationAddressTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/project_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ProjectTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/record_change_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RecordChangeTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/tip_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TipTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/tipping_policies_text_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TippingPoliciesTextTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/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 | -------------------------------------------------------------------------------- /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 | ActiveRecord::Migration.check_pending! 7 | 8 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 9 | # 10 | # Note: You'll currently still have to declare fixtures explicitly in integration tests 11 | # -- they do not yet inherit this setting 12 | fixtures :all 13 | 14 | # Add more helper methods to be used by all tests here... 15 | end 16 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigmike/peer4commit/c63313c7281d28186849adb23fae93873c52da33/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------