One more step closer to rails 4 compatibility - warvox - VoIP based wardialing tool, forked from rapid7/warvox.
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit 0475c3d988f08164826df337fd3db8464708b68a
 (DIR) parent 5015670e3a10cbb3c934858c0097b9d83b5e0ab0
 (HTM) Author: HD Moore <hd_moore@rapid7.com>
       Date:   Mon,  8 Sep 2014 01:52:47 -0500
       
       One more step closer to rails 4 compatibility
       
       Diffstat:
         M Gemfile                             |       5 +++--
         M Gemfile.lock                        |      21 ++++++++-------------
         A app/assets/datatables/jquery.dataT… |       0 
         A app/assets/datatables/jquery.datat… |       0 
         M app/assets/javascripts/application… |       2 +-
         M app/assets/javascripts/bootstrap.j… |       6 ++----
         M app/assets/stylesheets/application… |       1 +
         M app/assets/stylesheets/application… |       6 ++++--
         A app/assets/stylesheets/bootstrap_a… |       8 ++++++++
         M app/assets/stylesheets/bootstrap_a… |     330 +++++++++++++++----------------
         M app/controllers/analyze_controller… |      11 ++++-------
         M app/controllers/calls_controller.rb |       6 ++----
         M app/controllers/jobs_controller.rb  |       6 ++----
         M app/controllers/projects_controlle… |       6 ++----
         M app/controllers/providers_controll… |       3 +--
         M app/models/call.rb                  |      63 +++++++++++++++++++------------
         M app/views/layouts/application.html… |       2 +-
         M bin/resetpw                         |       2 +-
         M bin/worker.rb                       |      10 +++++-----
         M bin/worker_manager.rb               |      10 +++++-----
         M config/environments/development.rb  |       4 ----
         M config/environments/production.rb   |       3 ---
         M config/initializers/warvox.rb       |       2 +-
         A config/locales/en.bootstrap.yml     |      23 +++++++++++++++++++++++
         M config/routes.rb                    |      56 ++++++++++++++++----------------
       
       25 files changed, 302 insertions(+), 284 deletions(-)
       ---
 (DIR) diff --git a/Gemfile b/Gemfile
       @@ -1,6 +1,5 @@
        source 'http://rubygems.org'
        
       -ruby '1.9.3'
        gem 'unicorn'
        gem 'rails', '>=4.1.5', '< 5.0.0'
        gem 'pg', '>=0.17'
       @@ -10,15 +9,17 @@ gem 'jquery-datatables-rails', '>= 2.2.3'
        
        # Integer array support (until Rails 4.0 is released)
        # Git version required to work around this bug: https://github.com/dockyard/postgres_ext/issues/5
       -gem 'postgres_ext', :git => 'git://github.com/dockyard/postgres_ext.git'
       +# gem 'postgres_ext', :git => 'git://github.com/dockyard/postgres_ext.git'
        
        gem 'kissfft'
        gem 'rex', '~> 2.0.3'
        
       +gem 'bootstrap-sass', '>= 3.2.0'
        gem 'sass-rails',   '~> 4.0.2'
        gem 'coffee-rails', '~> 4.0.1'
        gem 'uglifier',     '>= 1.0.3'
        gem 'protected_attributes', '~> 1.0.7'
       +gem 'autoprefixer-rails'
        
        gem 'authlogic', '>= 3.4.0'
        gem 'rails-settings-cached', '>= 0.4.1'
 (DIR) diff --git a/Gemfile.lock b/Gemfile.lock
       @@ -1,17 +1,8 @@
        GIT
       -  remote: git://github.com/dockyard/postgres_ext.git
       -  revision: 57bded66d1faba2c9e3090bcae43f795ac208b16
       -  specs:
       -    postgres_ext (2.3.0)
       -      activerecord (>= 4.0.0)
       -      arel (>= 4.0.1)
       -      pg_array_parser (~> 0.0.9)
       -
       -GIT
          remote: git://github.com/hmoore-r7/reportable.git
       -  revision: f8532eeb704c511bb75b5bd4fd9656243c9b118b
       +  revision: 073bc1657ee301f4c961f14247ef1a58cf38bd1f
          specs:
       -    reportable (1.2.0)
       +    reportable (1.3.1)
              activerecord (>= 3.0)
              activesupport (>= 3.0.0)
        
       @@ -49,8 +40,12 @@ GEM
              activerecord (>= 3.2)
              activesupport (>= 3.2)
              request_store (~> 1.0)
       +    autoprefixer-rails (3.0.1.20140826)
       +      execjs
            bootstrap-navbar (2.2.0)
              gem_config (~> 0.3)
       +    bootstrap-sass (3.2.0.2)
       +      sass (~> 3.2)
            builder (3.2.2)
            coffee-rails (4.0.1)
              coffee-script (>= 2.2.0)
       @@ -98,7 +93,6 @@ GEM
            minitest (5.4.1)
            multi_json (1.10.1)
            pg (0.17.1)
       -    pg_array_parser (0.0.9)
            polyglot (0.3.5)
            protected_attributes (1.0.8)
              activemodel (>= 4.0.1, < 5.0)
       @@ -181,6 +175,8 @@ PLATFORMS
        
        DEPENDENCIES
          authlogic (>= 3.4.0)
       +  autoprefixer-rails
       +  bootstrap-sass (>= 3.2.0)
          coffee-rails (~> 4.0.1)
          dynamic_form (>= 1.1.4)
          formtastic (>= 3.0.0)
       @@ -190,7 +186,6 @@ DEPENDENCIES
          kissfft
          less-rails-bootstrap
          pg (>= 0.17)
       -  postgres_ext!
          protected_attributes (~> 1.0.7)
          psych_shield
          rails (>= 4.1.5, < 5.0.0)
 (DIR) diff --git a/app/assets/datatables/jquery.dataTables.css b/app/assets/datatables/jquery.dataTables.css
 (DIR) diff --git a/app/assets/datatables/jquery.datatables.css b/app/assets/datatables/jquery.datatables.css
 (DIR) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
       @@ -5,7 +5,7 @@
        //= require twitter/bootstrap
        //= require bootstrap-lightbox
        //= require dataTables/jquery.dataTables
       -//= require dataTables/jquery.dataTables.bootstrap
       +//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
        //= require dataTables.hiddenTitle
        //= require dataTables.filteringDelay
        //= require dataTables.fnReloadAjax
 (DIR) diff --git a/app/assets/javascripts/bootstrap.js.coffee b/app/assets/javascripts/bootstrap.js.coffee
       @@ -1,4 +1,3 @@
        jQuery ->
       -  $("a[rel=popover]").popover()
       -  $(".tooltip").tooltip()
       -  $("a[rel=tooltip]").tooltip()
       -\ No newline at end of file
       +  $("a[rel~=popover], .has-popover").popover()
       +  $("a[rel~=tooltip], .has-tooltip").tooltip()
 (DIR) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
       @@ -2,6 +2,7 @@
         * This is a manifest file that'll automatically include all the stylesheets available in this directory
         * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
         * the top of the compiled file, but it's generally better to create a new file per style scope.
       + *= require formtastic-bootstrap
         *= require_self
         *= require_tree .
        */
 (DIR) diff --git a/app/assets/stylesheets/application.css.scss.erb b/app/assets/stylesheets/application.css.scss.erb
       @@ -1,6 +1,6 @@
        /*
         *= require bootstrap_and_overrides
       - */
       +*/
        
        /*
         *= require_self
       @@ -8,9 +8,11 @@
         *= require formtastic-bootstrap
         *= require formtastic-overrides
         *= require bootstrap-lightbox
       - *= require dataTables/jquery.dataTables.bootstrap
       + *= require jquery.dataTables
        */
        
       +@import "bootstrap-sprockets";
       +@import "bootstrap";
        
        table.list {
          td.actions {
 (DIR) diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css b/app/assets/stylesheets/bootstrap_and_overrides.css
       @@ -0,0 +1,7 @@
       +/*
       +  =require twitter-bootstrap-static/bootstrap
       +
       +  Use Font Awesome icons (default)
       +  To use Glyphicons sprites instead of Font Awesome, replace with "require twitter-bootstrap-static/sprites"
       +  =require twitter-bootstrap-static/fontawesome
       +  */
       +\ No newline at end of file
 (DIR) diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less
       @@ -1,35 +1,23 @@
        @import "twitter/bootstrap/bootstrap";
       -@import "twitter/bootstrap/responsive";
        
        // Set the correct sprite paths
       -@iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings");
       -@iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white");
       +@iconSpritePath: image-url("twitter/bootstrap/glyphicons-halflings.png");
       +@iconWhiteSpritePath: image-url("twitter/bootstrap/glyphicons-halflings-white.png");
        
        // Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
       -// Note: If you use asset_path() here, your compiled bootstrap_and_overrides.css will not
       -//       have the proper paths. So for now we use the absolute path.
       -@fontAwesomeEotPath: asset-path("fontawesome-webfont.eot");
       -@fontAwesomeEotPath_iefix: asset-path("fontawesome-webfont.eot#iefix");
       -@fontAwesomeWoffPath: asset-path("fontawesome-webfont.woff");
       -@fontAwesomeTtfPath: asset-path("fontawesome-webfont.ttf");
       -@fontAwesomeSvgPath: asset-path("fontawesome-webfont.svg");
       +@fontAwesomeEotPath: font-url("fontawesome-webfont.eot");
       +@fontAwesomeEotPath_iefix: font-url("fontawesome-webfont.eot?#iefix");
       +@fontAwesomeWoffPath: font-url("fontawesome-webfont.woff");
       +@fontAwesomeTtfPath: font-url("fontawesome-webfont.ttf");
       +@fontAwesomeSvgPath: font-url("fontawesome-webfont.svg#fontawesomeregular");
        
        // Font Awesome
       -@import "fontawesome";
       +@import "fontawesome/font-awesome";
        
        // Glyphicons
       -//@import "twitter/bootstrap/sprites.less";
       +//@import "twitter/bootstrap/glyphicons.less";
        
        // Your custom LESS stylesheets goes here
       -//
       -// Since bootstrap was imported above you have access to its mixins which
       -// you may use and inherit here
       -//
       -// If you'd like to override bootstrap's own variables, you can do so here as well
       -// See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation
       -//
       -// Example:
       -// @linkColor: #ff0000;
        
        
        @sansFontFamily: "Trebuchet MS", Arial, Helvetica, sans-serif;
       @@ -38,6 +26,9 @@
        @darkGray: #29383f;
        @lightGray: #666666;
        @blue: #0197b8;
       +@white: #ffffff;
       +@yellow: yellow;
       +@red: red;
        
        @darkOrange: #BB4607;
        @lightOrange: #F8ECE6;
       @@ -66,166 +57,166 @@
        // Datatables
        
        .paginate_disabled_previous {
       -        display: none;
       +  display: none;
        }
        
        .paginate_disabled_next {
       -        display: none;
       +  display: none;
        }
        
        .paginate_enabled_previous {
       -        color: red;
       -        margin-right: 20px;
       +  color: red;
       +  margin-right: 20px;
        }
        
        .paginate_enabled_next {
       -        color: green;
       +  color: green;
        }
        
        // End of DataTables
        
        
        .sparkline {
       -        width: 100%;
       -        height: 30px;
       +  width: 100%;
       +  height: 30px;
        }
        
        .sparkline-title {
       -        text-align: center;
       -        font-size: 11px;
       +  text-align: center;
       +  font-size: 11px;
        }
        
        .call-detail {
       -        font-size: 10px;
       +  font-size: 10px;
        }
        
        .project-header {
       -        margin-bottom: 30px;
       -        border-bottom: 1px solid #eeeeee;
       +  margin-bottom: 30px;
       +  border-bottom: 1px solid #eeeeee;
        }
        
        .zoom {
       -        background-color: @orange;
       -        height: 40px;
       +  background-color: @orange;
       +  height: 40px;
        }
        
        
        .stat-box {
       -        padding: 10px;
       -        background-color: white;
       -        border: 2px solid @darkGray;
       -        font-size: 16px;
       -        font-weight: bold;
       -        color: @darkGray;
       -        width: 55px;
       -        margin: auto auto;
       -        text-align: center;
       -
       -        border-top-left-radius: 20px;
       -        border-top-right-radius: 20px;
       -        border-bottom-right-radius: 20px;
       -        border-bottom-left-radius: 20px;
       +  padding: 10px;
       +  background-color: white;
       +  border: 2px solid @darkGray;
       +  font-size: 16px;
       +  font-weight: bold;
       +  color: @darkGray;
       +  width: 55px;
       +  margin: auto auto;
       +  text-align: center;
       +
       +  border-top-left-radius: 20px;
       +  border-top-right-radius: 20px;
       +  border-bottom-right-radius: 20px;
       +  border-bottom-left-radius: 20px;
        }
        
        .stat-modem {
       -        background-color: white;
       -        border: 2px solid @red;
       -        color: @red;
       +  background-color: white;
       +  border: 2px solid @red;
       +  color: @red;
        }
        
        
        .stat-nodata {
       -        background-color: #f4f4f4;
       -        border: 2px solid @darkGray;
       -        color: @darkGray;
       -        font-size: 24px;
       +  background-color: #f4f4f4;
       +  border: 2px solid @darkGray;
       +  color: @darkGray;
       +  font-size: 24px;
        }
        
        .stat-completed {
       -        background-color: white;
       -        border: 2px solid @orange;
       -        color: @darkGray;
       +  background-color: white;
       +  border: 2px solid @orange;
       +  color: @darkGray;
        }
        
        .stat-voice {
       -        background-color: white;
       -        border: 2px solid @green;
       -        color: @darkGray;
       +  background-color: white;
       +  border: 2px solid @green;
       +  color: @darkGray;
        }
        
        .stat-voicemail {
       -        background-color: white;
       -        border: 2px solid @blue;
       -        color: @darkGray;
       +  background-color: white;
       +  border: 2px solid @blue;
       +  color: @darkGray;
        }
        
        .stat-fax {
       -        background-color: white;
       -        border: 2px solid @darkGray;
       -        color: @darkGray;
       +  background-color: white;
       +  border: 2px solid @darkGray;
       +  color: @darkGray;
        }
        
        .stat-modem {
       -        background-color: white;
       -        border: 2px solid @red;
       -        color: @darkGray;
       +  background-color: white;
       +  border: 2px solid @red;
       +  color: @darkGray;
        }
        
        
        .arrow-down {
       -        font-size: 18px;
       -        text-align: center;
       -        margin: auto auto;
       -        padding-top: 10px;
       -        padding-bottom: 10px;
       -        color: @darkOrange;
       +  font-size: 18px;
       +  text-align: center;
       +  margin: auto auto;
       +  padding-top: 10px;
       +  padding-bottom: 10px;
       +  color: @darkOrange;
        }
        
        .arrow-right {
       -        font-size: 22px;
       -        text-align: center;
       -        margin: auto auto;
       -        padding-top: 10px;
       -        padding-bottom: 10px;
       +  font-size: 22px;
       +  text-align: center;
       +  margin: auto auto;
       +  padding-top: 10px;
       +  padding-bottom: 10px;
        }
        
        .stat-subtitle {
       -        text-align: center;
       -        font-size: 12px;
       -        margin-top: 1px;
       +  text-align: center;
       +  font-size: 12px;
       +  margin-top: 1px;
        }
        
        .fwd_match_span {
       -        width: 100px;
       -        padding-left: 20px;
       -        padding-right: 20px;
       -        padding-bottom: 5px;
       -        padding-top: 5px;
       -        font-weight: bold;
       -        color: white;
       -        text-align: center;
       +  width: 100px;
       +  padding-left: 20px;
       +  padding-right: 20px;
       +  padding-bottom: 5px;
       +  padding-top: 5px;
       +  font-weight: bold;
       +  color: white;
       +  text-align: center;
        }
        
        .filter-label {
       -        line-height: 16px;
       -        font-size: 16px;
       -        margin-right: 5px;
       -        padding-top: 5px;
       -        font-weight: bold;
       +  line-height: 16px;
       +  font-size: 16px;
       +  margin-right: 5px;
       +  padding-top: 5px;
       +  font-weight: bold;
        }
        
        .filter-select {
       -        line-height: 14px;
       -        font-size: 14px;
       -        width: 120px;
       -        font-weight: bold;
       +  line-height: 14px;
       +  font-size: 14px;
       +  width: 120px;
       +  font-weight: bold;
        }
        
        .popover-title {
       -        line-height: 12px;
       -        font-size: 12px;
       -        text-align: center;
       -        font-weight: bold;
       +  line-height: 12px;
       +  font-size: 12px;
       +  text-align: center;
       +  font-weight: bold;
        }
        
        .popover-content {
       @@ -233,83 +224,83 @@
        }
        
        .xtooltip {
       -        border-bottom: 1px dotted @blue;
       -        cursor: help;
       +  border-bottom: 1px dotted @blue;
       +  cursor: help;
        }
        
        .xpopover {
       -        border-bottom: 1px dotted @blue;
       -        cursor: help;
       +  border-bottom: 1px dotted @blue;
       +  cursor: help;
        }
        
        .progress-bar {
       -        margin-bottom: -2px;
       +  margin-bottom: -2px;
        }
        
        .progress_pct {
       -        color: @darkGray;
       -        margin-left: 10px;
       -        font-weight: bold;
       +  color: @darkGray;
       +  margin-left: 10px;
       +  font-weight: bold;
        }
        
        .task_args_formatted {
       -        padding:10px;
       +  padding:10px;
        }
        
        .task_args_var {
       -        font-size: 12px;
       -        float: left;
       -        font-weight: bold;
       -        text-align: right;
       +  font-size: 12px;
       +  float: left;
       +  font-weight: bold;
       +  text-align: right;
        }
        .task_args_val {
       -        font-size: 14px;
       -        text-align: left;
       -        padding-left: 5px;
       -        float: auto;
       +  font-size: 14px;
       +  text-align: left;
       +  padding-left: 5px;
       +  float: auto;
        }
        .check-item {
       -        font-size: 18px;
       -        margin-bottom: 10px;
       +  font-size: 18px;
       +  margin-bottom: 10px;
        }
        .check-result {
       -        margin-left: 40px;
       -        width: 500px;
       -        font-size: 16px;
       +  margin-left: 40px;
       +  width: 500px;
       +  font-size: 16px;
        }
        
        
        // Hacks to override active drop-down item background color and hover
        .dropdown-menu .active > a {
       -        background-color: @white;
       -        background-image: none;
       -        color: @darkGray;
       +  background-color: @white;
       +  background-image: none;
       +  color: @darkGray;
        }
        
        // Do not make active icons white
        .dropdown-menu > .active > a > [class^="icon-"], .dropdown-menu > .active > a > [class*="icon-"] {
       -        color: @darkGray;
       -        background-image: url("/assets/twitter/bootstrap/glyphicons-halflings.png");
       +  color: @darkGray;
       +  background-image: url("/assets/twitter/bootstrap/glyphicons-halflings.png");
        }
        
        .dropdown-menu .active > a:hover {
       -        background-color: @orange;
       -        background-image: none;
       -        color: @white;
       +  background-color: @orange;
       +  background-image: none;
       +  color: @white;
        }
        
        .fconstrained {
       -        width: 400px;
       +  width: 400px;
        }
        
        
        .project_description {
       -        height: 100px;
       -        padding: 5px;
       +  height: 100px;
       +  padding: 5px;
        }
        .project_includes {
       -        height: 100px;
       -        padding: 5px;
       +  height: 100px;
       +  padding: 5px;
        }
        
        body#login {
       @@ -351,35 +342,35 @@ background-image: linear-gradient(to bottom, #EA5709 0%, #000000 100%);
        }
        
        #login-logo {
       -        margin-left: auto;
       -        margin-right: auto;
       -        width: 222px;
       -        height: 68px;
       -        margin-top: 60px;
       +  margin-left: auto;
       +  margin-right: auto;
       +  width: 222px;
       +  height: 68px;
       +  margin-top: 60px;
        }
        
        #box {
       -        margin-left: auto;
       -        margin-right: auto;
       -        width: 350px;
       -        background: white;
       -        padding: 25px;
       -        margin-top: 20px;
       -        border: 1px solid #4A1C04;
       -        margin-bottom: 30px;
       +  margin-left: auto;
       +  margin-right: auto;
       +  width: 350px;
       +  background: white;
       +  padding: 25px;
       +  margin-top: 20px;
       +  border: 1px solid #4A1C04;
       +  margin-bottom: 30px;
        }
        
        .login-label {
       -        width: 120px;
       -        margin-right: 10px;
       -        margin-bottom: 5px;
       -        text-align: right;
       -        font-weight: bold;
       -        float: left;
       +  width: 120px;
       +  margin-right: 10px;
       +  margin-bottom: 5px;
       +  text-align: right;
       +  font-weight: bold;
       +  float: left;
        }
        
        .btn-login {
       -        margin-left: 130px;
       +  margin-left: 130px;
        }
        
        .nav {
       @@ -389,7 +380,7 @@ background-image: linear-gradient(to bottom, #EA5709 0%, #000000 100%);
        }
        
        .content {
       -        margin-top: 40px;
       +  margin-top: 40px;
        }
        
        .accordion-toggle:hover {
       @@ -397,18 +388,18 @@ background-image: linear-gradient(to bottom, #EA5709 0%, #000000 100%);
        }
        
        h1 {
       -        font-size: 24px;
       -        line-height: 30px;
       +  font-size: 24px;
       +  line-height: 30px;
        }
        
        h2 {
       -        font-size: 20px;
       -        line-height: 24px;
       +  font-size: 20px;
       +  line-height: 24px;
        }
        
        h3 {
       -        font-size: 16px;
       -        line-height: 18px;
       +  font-size: 16px;
       +  line-height: 18px;
        }
        
        .header {
       @@ -481,4 +472,4 @@ table.dataTable tr td.sorting_1 {
        }
        .carousel-control {
          background: #fff;
       -}
       +}
       +\ No newline at end of file
 (DIR) diff --git a/app/controllers/analyze_controller.rb b/app/controllers/analyze_controller.rb
       @@ -1,9 +1,8 @@
        class AnalyzeController < ApplicationController
        
          def index
       -        @jobs = Job.paginate(
       +        @jobs = Job.order('id DESC').paginate(
                        :page => params[:page],
       -                :order => 'id DESC',
                        :per_page => 30
                )
          end
       @@ -14,14 +13,12 @@ class AnalyzeController < ApplicationController
                @shown    = params[:show]
        
                if request.format.html?
       -                ltypes = Call.find( :all, :select => 'DISTINCT line_type', :conditions => ["job_id = ?", @job_id] ).map{|r| r.line_type}
       +                ltypes = Call.select('DISTINCT line_type').where(:job_id => @job_id).map{|r| r.line_type}
                        res_types = {}
        
                        ltypes.each do |k|
                                next if not k
       -                        res_types[k.capitalize.to_sym] = Call.count(
       -                                :conditions => ['job_id = ? and line_type = ?', @job_id, k]
       -                        )
       +                        res_types[k.capitalize.to_sym] = Call.where(:job_id => @job_id, :line_type => k).count
                        end
        
                        @lines_by_type = res_types
       @@ -47,7 +44,7 @@ class AnalyzeController < ApplicationController
                  calls_search
        
                  @results_total_display_count = Call.where(@search_conditions).count()
       -      @results = Call.where(@search_conditions).includes(:provider).limit(@results_per_page).offset(@results_offset).order(calls_sort_option)
       +    @results = Call.where(@search_conditions).includes(:provider).limit(@results_per_page).offset(@results_offset).order(calls_sort_option)
        
            end
        
 (DIR) diff --git a/app/controllers/calls_controller.rb b/app/controllers/calls_controller.rb
       @@ -3,9 +3,8 @@ class CallsController < ApplicationController
          # GET /calls
          # GET /calls.xml
          def index
       -    @jobs = @project.jobs.where('task = ? AND completed_at IS NOT NULL', 'dialer').paginate(
       +    @jobs = @project.jobs.order('id DESC').where('task = ? AND completed_at IS NOT NULL', 'dialer').paginate(
                        :page => params[:page],
       -                :order => 'id DESC',
                        :per_page => 30
                )
        
       @@ -18,9 +17,8 @@ class CallsController < ApplicationController
          # GET /calls/1/view
          # GET /calls/1/view.xml
          def view
       -    @calls = Call.where(:job_id => params[:id]).paginate(
       +    @calls = Call.order('id DESC').where(:job_id => params[:id]).paginate(
                        :page => params[:page],
       -                :order => 'number ASC',
                        :per_page => 30
                )
        
 (DIR) diff --git a/app/controllers/jobs_controller.rb b/app/controllers/jobs_controller.rb
       @@ -7,9 +7,8 @@ class JobsController < ApplicationController
        
                        @submitted_jobs = Job.where(:status => ['submitted', 'scheduled'], :completed_at => nil)
                        @active_jobs    = Job.where(:status => 'running', :completed_at => nil)
       -                @inactive_jobs  = Job.where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
       +                @inactive_jobs  = Job.order('id DESC').where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
                                :page => params[:page],
       -                        :order => 'id DESC',
                                :per_page => 30
                        )
        
       @@ -27,9 +26,8 @@ class JobsController < ApplicationController
                end
        
                def results
       -                @jobs = @project.jobs.where('(task = ? OR task = ?) AND completed_at IS NOT NULL', 'dialer', 'import').paginate(
       +                @jobs = @project.jobs.order('id DESC').where('(task = ? OR task = ?) AND completed_at IS NOT NULL', 'dialer', 'import').paginate(
                                :page => params[:page],
       -                        :order => 'id DESC',
                                :per_page => 30
                        )
        
 (DIR) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
       @@ -1,9 +1,8 @@
        class ProjectsController < ApplicationController
        
                def index
       -                 @projects = Project.paginate(
       +                 @projects = Project.order('id DESC').paginate(
                                :page => params[:page],
       -                        :order => 'id DESC',
                                :per_page => 10
                        )
        
       @@ -18,9 +17,8 @@ class ProjectsController < ApplicationController
                def show
                        @project = Project.find(params[:id])
                        @active_jobs = @project.jobs.where(:status => 'running', :completed_at => nil)
       -                @inactive_jobs        = @project.jobs.where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
       +                @inactive_jobs        = @project.jobs.order('id DESC').where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
                                :page => params[:page],
       -                        :order => 'id DESC',
                                :per_page => 30
                        )
        
 (DIR) diff --git a/app/controllers/providers_controller.rb b/app/controllers/providers_controller.rb
       @@ -2,9 +2,8 @@ class ProvidersController < ApplicationController
        
          def index
        
       -           @providers = Provider.paginate(
       +           @providers = Provider.order('id DESC').paginate(
                        :page => params[:page],
       -                :order => 'id DESC',
                        :per_page => 10
                )
        
 (DIR) diff --git a/app/models/call.rb b/app/models/call.rb
       @@ -15,27 +15,42 @@ class Call < ActiveRecord::Base
          belongs_to :job
          has_one :call_medium, :dependent => :delete
        
       -  has_many :matches, :class_name => 'Call', :finder_sql => proc {
       -    'SELECT calls.*,  ' +
       -    "  (( icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}\'::int[] & calls.fprint::int[]) / icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}'::int[])::float ) * 100.0 ) AS matchscore " +
       -    'FROM calls ' +
       -    'WHERE icount(calls.fprint) > 0 AND ' +
       -    "calls.job_id = \'#{job_id}\' AND " +
       -    "calls.id != \'#{id}\' " +
       -#    "AND (( icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}\'::int[] & calls.fprint::int[]) / icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}'::int[])::float ) * 100.0 ) > 10.0 " +
       -    'ORDER BY matchscore DESC'
       -  }
       -
       -  has_many :matches_all_jobs, :class_name => 'Call', :finder_sql => proc {
       -    'SELECT calls.*,  ' +
       -    "  (( icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}\'::int[] & calls.fprint::int[]) / icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}\'::int[])::float ) * 100.0 ) AS matchscore " +
       -    'FROM calls ' +
       -    'WHERE icount(calls.fprint) > 0 AND ' +
       -    "calls.id != \'#{id}\' " +
       -#  "AND (( icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}\'::int[] & calls.fprint::int[]) / icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}'::int[])::float ) * 100.0 ) > 10.0 " +
       -    'ORDER BY matchscore DESC'
       -  }
       +  def matches
       +    #    "AND (( icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}\'::int[] & calls.fprint::int[]) / icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}'::int[])::float ) * 100.0 ) > 10.0 " +
       +    self.find_by_sql([
       +      'SELECT calls.*,  ' +
       +      "  (( icount(?::int[] & calls.fprint::int[]) / icount(?::int[])::float ) * 100.0 ) AS matchscore " +
       +      'FROM calls ' +
       +      'WHERE icount(calls.fprint) > 0 AND ' +
       +      "calls.job_id = ? AND " +
       +      "calls.id != ? " +
       +      'ORDER BY matchscore DESC',
       +      fprint_map,
       +      fprint_map,
       +      self.job_id,
       +      self.id
       +      ])
       +  end
       +
       +  def matches_all_jobs
        
       +    #    "AND (( icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}\'::int[] & calls.fprint::int[]) / icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}'::int[])::float ) * 100.0 ) > 10.0 " +
       +    self.find_by_sql([    
       +      'SELECT calls.*,  ' +
       +      "  (( icount(?::int[] & calls.fprint::int[]) / icount(?::int[])::float ) * 100.0 ) AS matchscore " +
       +      'FROM calls ' +
       +      'WHERE icount(calls.fprint) > 0 AND ' +
       +      "calls.id != ? " +
       +      'ORDER BY matchscore DESC',
       +      fprint_map,
       +      fprint_map,
       +      self.id
       +      ])    
       +  end
       +
       +  def fprint_map
       +    @fprint_map ||= "{" + fprint.map{|x| x.to_s}.join(",") + "}"
       +  end
        
          after_save :update_linked_line
        
       @@ -44,19 +59,19 @@ class Call < ActiveRecord::Base
            scope_limit = ""
            case scope
            when 'job'
       -      scope_limit = "calls.job_id = \'#{job_id}\' AND "
       +      scope_limit = "calls.job_id = \'#{job_id.to_i}\' AND "
            when 'project'
       -      scope_limit = "calls.project_id = \'#{project_id}\' AND "
       +      scope_limit = "calls.project_id = \'#{project_id.to_i}\' AND "
            end
        
            query =
              'SELECT calls.*,  ' +
       -      "  (( icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}\'::int[] & calls.fprint::int[]) / icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}'::int[])::float ) * 100.0 ) AS matchscore " +
       +      "  (( icount(\'{#{fprint_map}}\'::int[] & calls.fprint::int[]) / icount(\'{#{fprint_map}'::int[])::float ) * 100.0 ) AS matchscore " +
              'FROM calls ' +
              'WHERE icount(calls.fprint) > 0 AND ' +
              scope_limit +
              "calls.id != \'#{id}\' " +
       -      "AND (( icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}\'::int[] & calls.fprint::int[]) / icount(\'{#{fprint.map{|x| x.to_s}.join(",")}}'::int[])::float ) * 100.0 ) > #{min_match} " +
       +      "AND (( icount(\'#{fprint_map}\'::int[] & calls.fprint::int[]) / icount(\'#{fprint_map}\'::int[])::float ) * 100.0 ) > #{min_match.to_f} " +
              'ORDER BY matchscore DESC'
        
            Call.paginate_by_sql(query, :page => page, :per_page => per_page)
 (DIR) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
       @@ -57,7 +57,7 @@
                                                <% if Project.count > 0 %>
                                                        <%= drop_down_divider %>
                                                        <%= drop_down_header "Recent Projects" %>
       -                                                <% Project.find(:all, :order => 'ID DESC', :limit => 5).each do |project| %>
       +                                                <% Project.order('ID DESC').limit(5).each do |project| %>
                                                                <%= menu_item raw('<i class="icon-chevron-right"></i> ' + h(truncate(project.name, :length => 15))),project_path(project) %>
                                                        <% end %>
                                                <% end %>
 (DIR) diff --git a/bin/resetpw b/bin/resetpw
       @@ -36,7 +36,7 @@ def generate_password
        end
        
        
       -user = uname ? User.find_by_login(uname) : User.find(:first)
       +user = uname ? User.find_by_login(uname) : User.first
        if uname and not user
                $stderr.puts "[-] User #{uname} was not found"
                exit(1)
 (DIR) diff --git a/bin/worker.rb b/bin/worker.rb
       @@ -30,7 +30,7 @@ def stop
                        @task.stop() rescue nil
                end
                if @job
       -                Job.update_all({ :status => 'stopped', :completed_at => Time.now }, { :id => @job.id })
       +                Job.where(id: @job_id).update_all({ status: 'stopped', completed_at: Time.now })
                end
                exit(0)
        end
       @@ -51,7 +51,7 @@ trap("SIGTERM") { stop() }
        
        jid = jid.to_i
        
       -@job = Job.where(:id => jid).first
       +@job = Job.where(id: jid).first
        
        unless @job
                $stderr.puts "Error: Specified job not found"
       @@ -61,7 +61,7 @@ end
        
        $0 = "warvox worker: #{jid} "
        
       -Job.update_all({ :started_at => Time.now.utc, :status => 'running'}, { :id => @job.id })
       +Job.where(id: @job.id).update_all({ started_at: Time.now.utc, status: 'running'})
        
        args = Marshal.load(@job.args) rescue {}
        
       @@ -78,7 +78,7 @@ when 'analysis'
                @task = WarVOX::Jobs::Analysis.new(@job.id, args)
                @task.start
        else
       -        Job.update_all({ :error => 'unsupported', :status => 'error' }, { :id => @job.id })
       +        Job.where(id: @job.id).update_all({ error: 'unsupported', status: 'error' })
        end
        
        @job.update_progress(100)
       @@ -87,5 +87,5 @@ rescue ::SignalException, ::SystemExit
                raise $!
        rescue ::Exception => e
                WarVOX::Log.warn("Worker #{@job.id} #{@job.task} threw an exception: #{e.class} #{e} #{e.backtrace}")
       -        Job.update_all({ :error => "Exception: #{e.class} #{e}", :status => 'error', :completed_at => Time.now.utc }, { :id => @job.id })
       +        Job.where(id: @job.id).update_all({ error: "Exception: #{e.class} #{e}", status: 'error', completed_at: Time.now.utc })
        end
 (DIR) diff --git a/bin/worker_manager.rb b/bin/worker_manager.rb
       @@ -95,7 +95,7 @@ def clear_completed_jobs
                @jobs = @jobs.reject{|x| dead_pids.include?( x[:pid] ) }
        
                # Mark failed/crashed jobs as completed
       -        Job.update_all({ :completed_at => Time.now.utc }, { :id => dead_jids, :completed_at => nil })
       +        Job.where(id: dead_jids, completed_at: nil).update_all({completed_at: Time.now.utc})
        end
        
        def clear_stale_jobs
       @@ -131,21 +131,21 @@ def clear_stale_jobs
                # Mark these jobs as abandoned
                if dead.length > 0
                        WarVOX::Log.debug("Worker Manager is marking #{dead.length} jobs as abandoned")
       -                Job.update_all({ :locked_by => nil, :status => 'abandoned' }, { :id => dead })
       +                Job.where(:id => dead).update_all({locked_by: nil, status: 'abandoned'})
                end
        end
        
        def schedule_submitted_jobs
                loop do
                        # Look for a candidate job with no current owner
       -                j  = Job.where(:status => 'submitted', :locked_by => nil).limit(1).first
       +                j  = Job.where(status: 'submitted', locked_by: nil).limit(1).first
                        return unless j
        
                        # Try to get a lock on this job
       -                Job.update_all({:locked_by => @cookie, :locked_at => Time.now.utc, :status => 'scheduled'}, {:id => j.id, :locked_by => nil})
       +                Job.where(id: j.id, locked_by: nil).update_all({locked_by: @cookie, locked_at: Time.now.utc, status: 'scheduled'})
        
                        # See if we actually got the lock
       -                j  = Job.where(:id => j.id, :status => 'scheduled', :locked_by => @cookie).limit(1).first
       +                j  = Job.where(id: j.id, status: 'scheduled', locked_by: @cookie).limit(1).first
        
                        # Try again if we lost the race,
                        next unless j
 (DIR) diff --git a/config/environments/development.rb b/config/environments/development.rb
       @@ -27,10 +27,6 @@ Web::Application.configure do
        
          config.log_level = :debug
        
       -  # Log the query plan for queries taking more than this (works
       -  # with SQLite, MySQL, and PostgreSQL)
       -  config.active_record.auto_explain_threshold_in_seconds = 0.75
       -
          # Do not compress assets
          config.assets.compress = false
        
 (DIR) diff --git a/config/environments/production.rb b/config/environments/production.rb
       @@ -64,7 +64,4 @@ Web::Application.configure do
          # Eager load
          config.eager_load = true
        
       -  # Log the query plan for queries taking more than this (works
       -  # with SQLite, MySQL, and PostgreSQL)
       -  # config.active_record.auto_explain_threshold_in_seconds = 0.5
        end
 (DIR) diff --git a/config/initializers/warvox.rb b/config/initializers/warvox.rb
       @@ -1,3 +1,3 @@
        
        # Extend PostgreSQL
       -require 'postgres_ext'
       +# require 'postgres_ext'
 (DIR) diff --git a/config/locales/en.bootstrap.yml b/config/locales/en.bootstrap.yml
       @@ -0,0 +1,23 @@
       +# Sample localization file for English. Add more files in this directory for other locales.
       +# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
       +
       +en:
       +  breadcrumbs:
       +    application:
       +      root: "Index"
       +    pages:
       +      pages: "Pages"
       +  helpers:
       +    actions: "Actions"
       +    links:
       +      back: "Back"
       +      cancel: "Cancel"
       +      confirm: "Are you sure?"
       +      destroy: "Delete"
       +      new: "New"
       +      edit: "Edit"
       +    titles:
       +      edit: "Edit %{model}"
       +      save: "Save %{model}"
       +      new: "New %{model}"
       +      delete: "Delete %{model}"
 (DIR) diff --git a/config/routes.rb b/config/routes.rb
       @@ -1,44 +1,44 @@
        Web::Application.routes.draw do
        
       -  match "login" => "user_sessions#new", :as => "login"
       -  match "logout" => "user_sessions#destroy", :as => "logout"
       +  get "login" => "user_sessions#new", :as => "login"
       +  get "logout" => "user_sessions#destroy", :as => "logout"
        
          resources :user_sessions
        
       -  match  '/projects/:project_id/all'                    => 'projects#index', :as => :all_projects
       +  get  '/projects/:project_id/all'                    => 'projects#index', :as => :all_projects
        
        
       -  match  '/jobs/dial'            => 'jobs#new_dialer',   :as => :new_dialer_job
       -  match  '/projects/:project_id/jobs/dial'            => 'jobs#new_dialer',   :as => :new_dialer_project_job
       -  match  '/jobs/dialer'          => 'jobs#dialer',       :as => :dialer_job
       +  get  '/jobs/dial'            => 'jobs#new_dialer',   :as => :new_dialer_job
       +  get  '/projects/:project_id/jobs/dial'            => 'jobs#new_dialer',   :as => :new_dialer_project_job
       +  put  '/jobs/dialer'          => 'jobs#dialer',       :as => :dialer_job
        
       -  match  '/jobs/analyze'         => 'jobs#new_analyze',  :as => :new_analyze_job
       -  match  '/projects/:project_id/jobs/analyze'         => 'jobs#new_analyze',  :as => :new_analyze_project_job
       -  match  '/jobs/analyzer'        => 'jobs#analyzer',     :as => :analyzer_job
       +  get  '/jobs/analyze'         => 'jobs#new_analyze',  :as => :new_analyze_job
       +  get  '/projects/:project_id/jobs/analyze'         => 'jobs#new_analyze',  :as => :new_analyze_project_job
       +  put  '/jobs/analyzer'        => 'jobs#analyzer',     :as => :analyzer_job
        
       -  match  '/projects/:project_id/jobs/identify'        => 'jobs#new_identify', :as => :new_identify_project_job
       -  match  '/jobs/identifier'      => 'jobs#identifier',   :as => :identifier_job
       +  get  '/projects/:project_id/jobs/identify'        => 'jobs#new_identify', :as => :new_identify_project_job
       +  put  '/jobs/identifier'      => 'jobs#identifier',   :as => :identifier_job
        
       -  match  '/jobs/:id/stop'        => 'jobs#stop',         :as => :stop_job
       -  match  '/jobs/:id/calls/purge' => "jobs#purge_calls",  :as => :purge_calls_job
       +  get  '/jobs/:id/stop'        => 'jobs#stop',         :as => :stop_job
       +  post  '/jobs/:id/calls/purge' => "jobs#purge_calls",  :as => :purge_calls_job
        
       -  match  '/projects/:project_id/calls/purge' => "jobs#purge_calls",  :as => :purge_calls_project_job
       +  post  '/projects/:project_id/calls/purge' => "jobs#purge_calls",  :as => :purge_calls_project_job
        
       -  match  '/projects/:project_id/scans'          => 'jobs#results', :as => :results
       -  match  '/projects/:project_id/scans/:id'      => 'jobs#view_results', :as => :view_results
       -  match  '/projects/:project_id/scans/:id/analyze'    => 'jobs#analyze_job', :as => :analyze_job
       -  match  '/projects/:project_id/scans/:id/reanalyze'  => 'jobs#reanalyze_job', :as => :reanalyze_job
       +  get  '/projects/:project_id/scans'          => 'jobs#results', :as => :results
       +  get  '/projects/:project_id/scans/:id'      => 'jobs#view_results', :as => :view_results
       +  get  '/projects/:project_id/scans/:id/analyze'    => 'jobs#analyze_job', :as => :analyze_job
       +  get  '/projects/:project_id/scans/:id/reanalyze'  => 'jobs#reanalyze_job', :as => :reanalyze_job
        
       -  match  '/projects/:project_id/calls/analyze'      => 'jobs#analyze_project', :as => :analyze_project_job
       -  match  '/projects/:project_id/calls/identify'     => 'jobs#identify_project', :as => :identify_project_job
       +  get  '/projects/:project_id/calls/analyze'      => 'jobs#analyze_project', :as => :analyze_project_job
       +  get  '/projects/:project_id/calls/identify'     => 'jobs#identify_project', :as => :identify_project_job
        
        
       -  match '/projects/:project_id/analyze'             => 'analyze#index', :as => :analyze
       -  match '/call/:result_id.:type'                   => 'analyze#resource', :as => :resource_analyze
       -  match '/projects/:project_id/analyze/:id/view'    => 'analyze#view', :as => :view_analyze
       +  get '/projects/:project_id/analyze'             => 'analyze#index', :as => :analyze
       +  get '/call/:result_id.:type'                   => 'analyze#resource', :as => :resource_analyze
       +  get '/projects/:project_id/analyze/:id/view'    => 'analyze#view', :as => :view_analyze
        
       -  match '/projects/:project_id/analyze/:job_id/:call_id/matches'    => 'analyze#view_matches', :as => :view_matches
       -  match '/projects/:project_id/analyze/:call_id/matches'    => 'analyze#view_matches', :as => :view_matches_project
       +  get '/projects/:project_id/analyze/:job_id/:call_id/matches'    => 'analyze#view_matches', :as => :view_matches
       +  get '/projects/:project_id/analyze/:call_id/matches'    => 'analyze#view_matches', :as => :view_matches_project
        
          resources :settings
          resources :providers
       @@ -47,9 +47,9 @@ Web::Application.routes.draw do
          resources :jobs
          resources :calls
        
       -  match '/about'               => 'home#about', :as => :about
       -  match '/help'                => 'home#help',  :as => :help
       -  match '/check'               => 'home#check', :as => :check
       +  get '/about'               => 'home#about', :as => :about
       +  get '/help'                => 'home#help',  :as => :help
       +  get '/check'               => 'home#check', :as => :check
        
          root :to => "projects#index"
        end