diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..71f82cb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+.bundle/
+log/*.log
+pkg/
+test/dummy/db/*.sqlite3
+test/dummy/db/*.sqlite3-journal
+test/dummy/log/*.log
+test/dummy/tmp/
diff --git a/.idea/.rakeTasks b/.idea/.rakeTasks
new file mode 100644
index 0000000..c6865d9
--- /dev/null
+++ b/.idea/.rakeTasks
@@ -0,0 +1,7 @@
+
+
diff --git a/.idea/boxroom-engine.iml b/.idea/boxroom-engine.iml
new file mode 100644
index 0000000..31a7fe2
--- /dev/null
+++ b/.idea/boxroom-engine.iml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..0eefe32
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..de6e841
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..e62cd16
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..b08841a
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1516812529653
+
+
+ 1516812529653
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..40aeee6
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,14 @@
+source 'https://rubygems.org'
+
+# Declare your gem's dependencies in boxroom.gemspec.
+# Bundler will treat runtime dependencies like base dependencies, and
+# development dependencies will be added by default to the :development group.
+gemspec
+
+# Declare any dependencies that are still in development here instead of in
+# your gemspec. These might include edge Rails or gems from your path or
+# Git. Remember to move these dependencies to your gemspec before releasing
+# your gem to rubygems.org.
+
+# To use a debugger
+# gem 'byebug', group: [:development, :test]
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..de6d594
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,152 @@
+PATH
+ remote: .
+ specs:
+ boxroom (0.1.0)
+ acts_as_tree
+ dynamic_form
+ jquery-fileupload-rails
+ paperclip
+ rails (~> 5.0.2)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ actioncable (5.0.2)
+ actionpack (= 5.0.2)
+ nio4r (>= 1.2, < 3.0)
+ websocket-driver (~> 0.6.1)
+ actionmailer (5.0.2)
+ actionpack (= 5.0.2)
+ actionview (= 5.0.2)
+ activejob (= 5.0.2)
+ mail (~> 2.5, >= 2.5.4)
+ rails-dom-testing (~> 2.0)
+ actionpack (5.0.2)
+ actionview (= 5.0.2)
+ activesupport (= 5.0.2)
+ rack (~> 2.0)
+ rack-test (~> 0.6.3)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
+ actionview (5.0.2)
+ activesupport (= 5.0.2)
+ builder (~> 3.1)
+ erubis (~> 2.7.0)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
+ activejob (5.0.2)
+ activesupport (= 5.0.2)
+ globalid (>= 0.3.6)
+ activemodel (5.0.2)
+ activesupport (= 5.0.2)
+ activerecord (5.0.2)
+ activemodel (= 5.0.2)
+ activesupport (= 5.0.2)
+ arel (~> 7.0)
+ activesupport (5.0.2)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (~> 0.7)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ acts_as_tree (2.7.0)
+ activerecord (>= 3.0.0)
+ arel (7.1.4)
+ builder (3.2.3)
+ climate_control (0.2.0)
+ cocaine (0.5.8)
+ climate_control (>= 0.0.3, < 1.0)
+ concurrent-ruby (1.0.5)
+ crass (1.0.2)
+ dynamic_form (1.1.4)
+ erubis (2.7.0)
+ ffi (1.9.18)
+ globalid (0.4.1)
+ activesupport (>= 4.2.0)
+ i18n (0.9.0)
+ concurrent-ruby (~> 1.0)
+ jquery-fileupload-rails (0.4.7)
+ actionpack (>= 3.1)
+ railties (>= 3.1)
+ sass (>= 3.2)
+ loofah (2.1.1)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ mail (2.7.0)
+ mini_mime (>= 0.1.1)
+ method_source (0.8.2)
+ mime-types (3.1)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2016.0521)
+ mimemagic (0.3.2)
+ mini_mime (0.1.4)
+ mini_portile2 (2.3.0)
+ minitest (5.10.3)
+ nio4r (2.0.0)
+ nokogiri (1.8.1)
+ mini_portile2 (~> 2.3.0)
+ paperclip (5.1.0)
+ activemodel (>= 4.2.0)
+ activesupport (>= 4.2.0)
+ cocaine (~> 0.5.5)
+ mime-types
+ mimemagic (~> 0.3.0)
+ rack (2.0.1)
+ rack-test (0.6.3)
+ rack (>= 1.0)
+ rails (5.0.2)
+ actioncable (= 5.0.2)
+ actionmailer (= 5.0.2)
+ actionpack (= 5.0.2)
+ actionview (= 5.0.2)
+ activejob (= 5.0.2)
+ activemodel (= 5.0.2)
+ activerecord (= 5.0.2)
+ activesupport (= 5.0.2)
+ bundler (>= 1.3.0, < 2.0)
+ railties (= 5.0.2)
+ sprockets-rails (>= 2.0.0)
+ rails-dom-testing (2.0.2)
+ activesupport (>= 4.2.0, < 6.0)
+ nokogiri (~> 1.6)
+ rails-html-sanitizer (1.0.3)
+ loofah (~> 2.0)
+ railties (5.0.2)
+ actionpack (= 5.0.2)
+ activesupport (= 5.0.2)
+ method_source
+ rake (>= 0.8.7)
+ thor (>= 0.18.1, < 2.0)
+ rake (12.2.1)
+ rb-fsevent (0.10.2)
+ rb-inotify (0.9.10)
+ ffi (>= 0.5.0, < 2)
+ sass (3.5.3)
+ sass-listen (~> 4.0.0)
+ sass-listen (4.0.0)
+ rb-fsevent (~> 0.9, >= 0.9.4)
+ rb-inotify (~> 0.9, >= 0.9.7)
+ sprockets (3.7.1)
+ concurrent-ruby (~> 1.0)
+ rack (> 1, < 3)
+ sprockets-rails (3.2.1)
+ actionpack (>= 4.0)
+ activesupport (>= 4.0)
+ sprockets (>= 3.0.0)
+ sqlite3 (1.3.11)
+ thor (0.20.0)
+ thread_safe (0.3.6)
+ tzinfo (1.2.4)
+ thread_safe (~> 0.1)
+ websocket-driver (0.6.5)
+ websocket-extensions (>= 0.1.0)
+ websocket-extensions (0.1.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ boxroom!
+ sqlite3
+
+BUNDLED WITH
+ 1.16.0
diff --git a/MIT-LICENSE b/MIT-LICENSE
new file mode 100644
index 0000000..5aa9b99
--- /dev/null
+++ b/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright 2018 Serge Koba
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index 6766e93..151fa4d 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,29 @@
-# boxroom-engine
-Boxroom web file manager Rails engine
+# Boxroom
+This is a Rails engine built based on code of [Boxroom](https://github.com/mischa78/boxroom) project.
+
+# Features
+
+
+## Install
+- add to Gemfile
+- run `rails boxroom:install:migrations`
+- run `rails db:migrate`
+
+## Config
+- Create `config/initializers/boxroom.rb`
+```ruby
+Boxroom.configure do |config|
+ config.site_name = 'Boxroom'
+ config.logo = 'boxroom/logo.png'
+end
+```
+- mount engine in `config/routes.rb`
+```ruby
+mount Boxroom::Engine => "/boxroom"
+```
+
+## Contributing
+Please feel free to leave an issue or PR.
+
+## License
+The engine is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..add3afd
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,36 @@
+begin
+ require 'bundler/setup'
+rescue LoadError
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
+end
+
+require 'rdoc/task'
+
+RDoc::Task.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'Boxroom'
+ rdoc.options << '--line-numbers'
+ rdoc.rdoc_files.include('README.md')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
+load 'rails/tasks/engine.rake'
+
+
+load 'rails/tasks/statistics.rake'
+
+
+
+require 'bundler/gem_tasks'
+
+require 'rake/testtask'
+
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'test'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = false
+end
+
+
+task default: :test
diff --git a/app/assets/config/boxroom_manifest.js b/app/assets/config/boxroom_manifest.js
new file mode 100644
index 0000000..6ca6727
--- /dev/null
+++ b/app/assets/config/boxroom_manifest.js
@@ -0,0 +1,2 @@
+//= link_directory ../javascripts/boxroom .js
+//= link_directory ../stylesheets/boxroom .css
diff --git a/app/assets/images/boxroom/.keep b/app/assets/images/boxroom/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/app/assets/images/boxroom/clipboard.png b/app/assets/images/boxroom/clipboard.png
new file mode 100755
index 0000000..08647f1
Binary files /dev/null and b/app/assets/images/boxroom/clipboard.png differ
diff --git a/app/assets/images/boxroom/clipboard_add.png b/app/assets/images/boxroom/clipboard_add.png
new file mode 100755
index 0000000..1d0841d
Binary files /dev/null and b/app/assets/images/boxroom/clipboard_add.png differ
diff --git a/app/assets/images/boxroom/copy.png b/app/assets/images/boxroom/copy.png
new file mode 100755
index 0000000..3836257
Binary files /dev/null and b/app/assets/images/boxroom/copy.png differ
diff --git a/app/assets/images/boxroom/delete.png b/app/assets/images/boxroom/delete.png
new file mode 100755
index 0000000..70b59dc
Binary files /dev/null and b/app/assets/images/boxroom/delete.png differ
diff --git a/app/assets/images/boxroom/edit.png b/app/assets/images/boxroom/edit.png
new file mode 100755
index 0000000..cf0ef85
Binary files /dev/null and b/app/assets/images/boxroom/edit.png differ
diff --git a/app/assets/images/boxroom/exclamation.png b/app/assets/images/boxroom/exclamation.png
new file mode 100755
index 0000000..721ba1e
Binary files /dev/null and b/app/assets/images/boxroom/exclamation.png differ
diff --git a/app/assets/images/boxroom/extend.png b/app/assets/images/boxroom/extend.png
new file mode 100755
index 0000000..a305904
Binary files /dev/null and b/app/assets/images/boxroom/extend.png differ
diff --git a/app/assets/images/boxroom/failed.png b/app/assets/images/boxroom/failed.png
new file mode 100755
index 0000000..7a368df
Binary files /dev/null and b/app/assets/images/boxroom/failed.png differ
diff --git a/app/assets/images/boxroom/file.png b/app/assets/images/boxroom/file.png
new file mode 100755
index 0000000..75f92b0
Binary files /dev/null and b/app/assets/images/boxroom/file.png differ
diff --git a/app/assets/images/boxroom/file_add.png b/app/assets/images/boxroom/file_add.png
new file mode 100755
index 0000000..df35ed6
Binary files /dev/null and b/app/assets/images/boxroom/file_add.png differ
diff --git a/app/assets/images/boxroom/fileicons/7z.png b/app/assets/images/boxroom/fileicons/7z.png
new file mode 100755
index 0000000..6d6333a
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/7z.png differ
diff --git a/app/assets/images/boxroom/fileicons/ai.png b/app/assets/images/boxroom/fileicons/ai.png
new file mode 100755
index 0000000..9500998
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/ai.png differ
diff --git a/app/assets/images/boxroom/fileicons/aif.png b/app/assets/images/boxroom/fileicons/aif.png
new file mode 100755
index 0000000..73ce1ca
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/aif.png differ
diff --git a/app/assets/images/boxroom/fileicons/aiff.png b/app/assets/images/boxroom/fileicons/aiff.png
new file mode 100755
index 0000000..73ce1ca
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/aiff.png differ
diff --git a/app/assets/images/boxroom/fileicons/audio.png b/app/assets/images/boxroom/fileicons/audio.png
new file mode 100755
index 0000000..73ce1ca
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/audio.png differ
diff --git a/app/assets/images/boxroom/fileicons/bz2.png b/app/assets/images/boxroom/fileicons/bz2.png
new file mode 100755
index 0000000..6d6333a
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/bz2.png differ
diff --git a/app/assets/images/boxroom/fileicons/c.png b/app/assets/images/boxroom/fileicons/c.png
new file mode 100755
index 0000000..7aca3f8
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/c.png differ
diff --git a/app/assets/images/boxroom/fileicons/conf.png b/app/assets/images/boxroom/fileicons/conf.png
new file mode 100755
index 0000000..ed841a0
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/conf.png differ
diff --git a/app/assets/images/boxroom/fileicons/cpp.png b/app/assets/images/boxroom/fileicons/cpp.png
new file mode 100755
index 0000000..d398622
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/cpp.png differ
diff --git a/app/assets/images/boxroom/fileicons/cs.png b/app/assets/images/boxroom/fileicons/cs.png
new file mode 100755
index 0000000..7745561
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/cs.png differ
diff --git a/app/assets/images/boxroom/fileicons/css.png b/app/assets/images/boxroom/fileicons/css.png
new file mode 100755
index 0000000..b5769e6
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/css.png differ
diff --git a/app/assets/images/boxroom/fileicons/csv.png b/app/assets/images/boxroom/fileicons/csv.png
new file mode 100755
index 0000000..99eb086
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/csv.png differ
diff --git a/app/assets/images/boxroom/fileicons/divx.png b/app/assets/images/boxroom/fileicons/divx.png
new file mode 100755
index 0000000..c8bd259
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/divx.png differ
diff --git a/app/assets/images/boxroom/fileicons/doc.png b/app/assets/images/boxroom/fileicons/doc.png
new file mode 100755
index 0000000..1beb2e5
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/doc.png differ
diff --git a/app/assets/images/boxroom/fileicons/docx.png b/app/assets/images/boxroom/fileicons/docx.png
new file mode 100755
index 0000000..1beb2e5
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/docx.png differ
diff --git a/app/assets/images/boxroom/fileicons/dot.png b/app/assets/images/boxroom/fileicons/dot.png
new file mode 100755
index 0000000..1beb2e5
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/dot.png differ
diff --git a/app/assets/images/boxroom/fileicons/fla.png b/app/assets/images/boxroom/fileicons/fla.png
new file mode 100755
index 0000000..152da35
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/fla.png differ
diff --git a/app/assets/images/boxroom/fileicons/gif.png b/app/assets/images/boxroom/fileicons/gif.png
new file mode 100755
index 0000000..c485c20
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/gif.png differ
diff --git a/app/assets/images/boxroom/fileicons/gz.png b/app/assets/images/boxroom/fileicons/gz.png
new file mode 100755
index 0000000..6d6333a
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/gz.png differ
diff --git a/app/assets/images/boxroom/fileicons/htm.png b/app/assets/images/boxroom/fileicons/htm.png
new file mode 100755
index 0000000..f355d31
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/htm.png differ
diff --git a/app/assets/images/boxroom/fileicons/html.png b/app/assets/images/boxroom/fileicons/html.png
new file mode 100755
index 0000000..f355d31
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/html.png differ
diff --git a/app/assets/images/boxroom/fileicons/image.png b/app/assets/images/boxroom/fileicons/image.png
new file mode 100755
index 0000000..c485c20
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/image.png differ
diff --git a/app/assets/images/boxroom/fileicons/java.png b/app/assets/images/boxroom/fileicons/java.png
new file mode 100755
index 0000000..d398622
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/java.png differ
diff --git a/app/assets/images/boxroom/fileicons/jpeg.png b/app/assets/images/boxroom/fileicons/jpeg.png
new file mode 100755
index 0000000..c485c20
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/jpeg.png differ
diff --git a/app/assets/images/boxroom/fileicons/jpg.png b/app/assets/images/boxroom/fileicons/jpg.png
new file mode 100755
index 0000000..c485c20
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/jpg.png differ
diff --git a/app/assets/images/boxroom/fileicons/js.png b/app/assets/images/boxroom/fileicons/js.png
new file mode 100755
index 0000000..d398622
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/js.png differ
diff --git a/app/assets/images/boxroom/fileicons/mdb.png b/app/assets/images/boxroom/fileicons/mdb.png
new file mode 100755
index 0000000..4f08873
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/mdb.png differ
diff --git a/app/assets/images/boxroom/fileicons/mdbx.png b/app/assets/images/boxroom/fileicons/mdbx.png
new file mode 100755
index 0000000..4f08873
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/mdbx.png differ
diff --git a/app/assets/images/boxroom/fileicons/mov.png b/app/assets/images/boxroom/fileicons/mov.png
new file mode 100755
index 0000000..c8bd259
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/mov.png differ
diff --git a/app/assets/images/boxroom/fileicons/mp3.png b/app/assets/images/boxroom/fileicons/mp3.png
new file mode 100755
index 0000000..73ce1ca
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/mp3.png differ
diff --git a/app/assets/images/boxroom/fileicons/mpg.png b/app/assets/images/boxroom/fileicons/mpg.png
new file mode 100755
index 0000000..c8bd259
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/mpg.png differ
diff --git a/app/assets/images/boxroom/fileicons/ogg.png b/app/assets/images/boxroom/fileicons/ogg.png
new file mode 100755
index 0000000..73ce1ca
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/ogg.png differ
diff --git a/app/assets/images/boxroom/fileicons/pdf.png b/app/assets/images/boxroom/fileicons/pdf.png
new file mode 100755
index 0000000..c68ab66
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/pdf.png differ
diff --git a/app/assets/images/boxroom/fileicons/php.png b/app/assets/images/boxroom/fileicons/php.png
new file mode 100755
index 0000000..75938d2
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/php.png differ
diff --git a/app/assets/images/boxroom/fileicons/pl.png b/app/assets/images/boxroom/fileicons/pl.png
new file mode 100755
index 0000000..d398622
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/pl.png differ
diff --git a/app/assets/images/boxroom/fileicons/png.png b/app/assets/images/boxroom/fileicons/png.png
new file mode 100755
index 0000000..c485c20
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/png.png differ
diff --git a/app/assets/images/boxroom/fileicons/ppt.png b/app/assets/images/boxroom/fileicons/ppt.png
new file mode 100755
index 0000000..134a007
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/ppt.png differ
diff --git a/app/assets/images/boxroom/fileicons/pptx.png b/app/assets/images/boxroom/fileicons/pptx.png
new file mode 100755
index 0000000..134a007
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/pptx.png differ
diff --git a/app/assets/images/boxroom/fileicons/ps.png b/app/assets/images/boxroom/fileicons/ps.png
new file mode 100755
index 0000000..7b8b9b0
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/ps.png differ
diff --git a/app/assets/images/boxroom/fileicons/py.png b/app/assets/images/boxroom/fileicons/py.png
new file mode 100755
index 0000000..d398622
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/py.png differ
diff --git a/app/assets/images/boxroom/fileicons/ram.png b/app/assets/images/boxroom/fileicons/ram.png
new file mode 100755
index 0000000..c8bd259
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/ram.png differ
diff --git a/app/assets/images/boxroom/fileicons/rar.png b/app/assets/images/boxroom/fileicons/rar.png
new file mode 100755
index 0000000..6d6333a
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/rar.png differ
diff --git a/app/assets/images/boxroom/fileicons/rb.png b/app/assets/images/boxroom/fileicons/rb.png
new file mode 100755
index 0000000..7db6ba0
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/rb.png differ
diff --git a/app/assets/images/boxroom/fileicons/rm.png b/app/assets/images/boxroom/fileicons/rm.png
new file mode 100755
index 0000000..73ce1ca
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/rm.png differ
diff --git a/app/assets/images/boxroom/fileicons/rtf.png b/app/assets/images/boxroom/fileicons/rtf.png
new file mode 100755
index 0000000..ed841a0
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/rtf.png differ
diff --git a/app/assets/images/boxroom/fileicons/sql.png b/app/assets/images/boxroom/fileicons/sql.png
new file mode 100755
index 0000000..ea232a7
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/sql.png differ
diff --git a/app/assets/images/boxroom/fileicons/swf.png b/app/assets/images/boxroom/fileicons/swf.png
new file mode 100755
index 0000000..3070f05
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/swf.png differ
diff --git a/app/assets/images/boxroom/fileicons/tar.png b/app/assets/images/boxroom/fileicons/tar.png
new file mode 100755
index 0000000..6d6333a
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/tar.png differ
diff --git a/app/assets/images/boxroom/fileicons/tgz.png b/app/assets/images/boxroom/fileicons/tgz.png
new file mode 100755
index 0000000..6d6333a
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/tgz.png differ
diff --git a/app/assets/images/boxroom/fileicons/txt.png b/app/assets/images/boxroom/fileicons/txt.png
new file mode 100755
index 0000000..ed841a0
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/txt.png differ
diff --git a/app/assets/images/boxroom/fileicons/video.png b/app/assets/images/boxroom/fileicons/video.png
new file mode 100755
index 0000000..c8bd259
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/video.png differ
diff --git a/app/assets/images/boxroom/fileicons/wav.png b/app/assets/images/boxroom/fileicons/wav.png
new file mode 100755
index 0000000..73ce1ca
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/wav.png differ
diff --git a/app/assets/images/boxroom/fileicons/wma.png b/app/assets/images/boxroom/fileicons/wma.png
new file mode 100755
index 0000000..73ce1ca
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/wma.png differ
diff --git a/app/assets/images/boxroom/fileicons/wmv.png b/app/assets/images/boxroom/fileicons/wmv.png
new file mode 100755
index 0000000..c8bd259
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/wmv.png differ
diff --git a/app/assets/images/boxroom/fileicons/xls.png b/app/assets/images/boxroom/fileicons/xls.png
new file mode 100755
index 0000000..4403681
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/xls.png differ
diff --git a/app/assets/images/boxroom/fileicons/xlsx.png b/app/assets/images/boxroom/fileicons/xlsx.png
new file mode 100755
index 0000000..4403681
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/xlsx.png differ
diff --git a/app/assets/images/boxroom/fileicons/xml.png b/app/assets/images/boxroom/fileicons/xml.png
new file mode 100755
index 0000000..b5769e6
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/xml.png differ
diff --git a/app/assets/images/boxroom/fileicons/xvid.png b/app/assets/images/boxroom/fileicons/xvid.png
new file mode 100755
index 0000000..c8bd259
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/xvid.png differ
diff --git a/app/assets/images/boxroom/fileicons/zip.png b/app/assets/images/boxroom/fileicons/zip.png
new file mode 100755
index 0000000..6d6333a
Binary files /dev/null and b/app/assets/images/boxroom/fileicons/zip.png differ
diff --git a/app/assets/images/boxroom/folder.png b/app/assets/images/boxroom/folder.png
new file mode 100644
index 0000000..f0c7a97
Binary files /dev/null and b/app/assets/images/boxroom/folder.png differ
diff --git a/app/assets/images/boxroom/folder_add.png b/app/assets/images/boxroom/folder_add.png
new file mode 100644
index 0000000..1008981
Binary files /dev/null and b/app/assets/images/boxroom/folder_add.png differ
diff --git a/app/assets/images/boxroom/group.png b/app/assets/images/boxroom/group.png
new file mode 100755
index 0000000..a6aae04
Binary files /dev/null and b/app/assets/images/boxroom/group.png differ
diff --git a/app/assets/images/boxroom/group_add.png b/app/assets/images/boxroom/group_add.png
new file mode 100644
index 0000000..de1ef7b
Binary files /dev/null and b/app/assets/images/boxroom/group_add.png differ
diff --git a/app/assets/images/boxroom/group_grey.png b/app/assets/images/boxroom/group_grey.png
new file mode 100644
index 0000000..644dc3d
Binary files /dev/null and b/app/assets/images/boxroom/group_grey.png differ
diff --git a/app/assets/images/boxroom/information.png b/app/assets/images/boxroom/information.png
new file mode 100755
index 0000000..5d353a1
Binary files /dev/null and b/app/assets/images/boxroom/information.png differ
diff --git a/app/assets/images/boxroom/logo.png b/app/assets/images/boxroom/logo.png
new file mode 100644
index 0000000..5ba0687
Binary files /dev/null and b/app/assets/images/boxroom/logo.png differ
diff --git a/app/assets/images/boxroom/move.png b/app/assets/images/boxroom/move.png
new file mode 100755
index 0000000..d59a9bd
Binary files /dev/null and b/app/assets/images/boxroom/move.png differ
diff --git a/app/assets/images/boxroom/permissions.png b/app/assets/images/boxroom/permissions.png
new file mode 100755
index 0000000..7e47c79
Binary files /dev/null and b/app/assets/images/boxroom/permissions.png differ
diff --git a/app/assets/images/boxroom/share.png b/app/assets/images/boxroom/share.png
new file mode 100755
index 0000000..e3071ef
Binary files /dev/null and b/app/assets/images/boxroom/share.png differ
diff --git a/app/assets/images/boxroom/spinner.gif b/app/assets/images/boxroom/spinner.gif
new file mode 100755
index 0000000..bdcb518
Binary files /dev/null and b/app/assets/images/boxroom/spinner.gif differ
diff --git a/app/assets/images/boxroom/tick.png b/app/assets/images/boxroom/tick.png
new file mode 100755
index 0000000..a7d7a96
Binary files /dev/null and b/app/assets/images/boxroom/tick.png differ
diff --git a/app/assets/images/boxroom/user.png b/app/assets/images/boxroom/user.png
new file mode 100755
index 0000000..274d6b9
Binary files /dev/null and b/app/assets/images/boxroom/user.png differ
diff --git a/app/assets/images/boxroom/user_add.png b/app/assets/images/boxroom/user_add.png
new file mode 100755
index 0000000..9f6c0f5
Binary files /dev/null and b/app/assets/images/boxroom/user_add.png differ
diff --git a/app/assets/javascripts/boxroom/application.js.coffee b/app/assets/javascripts/boxroom/application.js.coffee
new file mode 100644
index 0000000..1bad5cd
--- /dev/null
+++ b/app/assets/javascripts/boxroom/application.js.coffee
@@ -0,0 +1,51 @@
+//= require jquery
+//= require jquery_ujs
+//= require jquery-fileupload/basic
+//= require jquery-fileupload/vendor/tmpl
+//= require_self
+//= require_tree .
+
+$(window).load ->
+ fadeout '#notice'
+ fadeout '#alert'
+
+jQuery ->
+ $('.back_link').on 'click', (e) ->
+ e.preventDefault()
+ show_element '#files_and_folders'
+
+ $('.permissions_link').on 'click', (e) ->
+ e.preventDefault()
+ show_element '#permissions'
+
+ $('.clipboard_link').on 'click', (e) ->
+ e.preventDefault()
+ show_element '#clipboard'
+
+ $('.emails_to_share_with').on 'change', (e) ->
+ update_counter e.target
+
+ $('.emails_to_share_with').on 'keyup', (e) ->
+ update_counter e.target
+
+fadeout = (el) ->
+ $(el).delay(3000).fadeOut('slow')
+
+show_element = (el) ->
+ el = '#files_and_folders' if $(el).is(':visible')
+
+ elements = ['#files_and_folders', '#permissions', '#clipboard']
+ elements.splice elements.indexOf(el), 1
+ hide_elements elements
+
+ $(el).slideDown('slow')
+ $("#{el}_link").removeClass('folder_menu').addClass('highlight')
+
+hide_elements = (elements) ->
+ for element in elements
+ $(element).slideUp('slow') if $(element).is(':visible')
+ $("#{element}_link").removeClass('highlight').addClass('folder_menu')
+
+update_counter = (el) ->
+ $('#counter').html el.value.length
+ $('#counter').css 'color', if el.value.length > 255 then '#F00' else '#000'
diff --git a/app/assets/javascripts/boxroom/files.js.coffee b/app/assets/javascripts/boxroom/files.js.coffee
new file mode 100644
index 0000000..236d074
--- /dev/null
+++ b/app/assets/javascripts/boxroom/files.js.coffee
@@ -0,0 +1,28 @@
+jQuery ->
+ $('#new_user_file').fileupload
+ dataType: 'script'
+ add: (e, data) ->
+ $('#user_file_attachment').prop('disabled', true)
+ file = data.files[0]
+ folder = $('#target_folder_id').val()
+ $.getJSON "/file_exists?name=#{encodeURIComponent(file.name)}&folder=#{encodeURIComponent(folder)}", (exists) ->
+ data.context = $(tmpl("template-upload", file).trim())
+ $('#progress').append(data.context)
+ if exists
+ data.context.find('.spinner').hide()
+ data.context.find('.failed').show()
+ data.context.find('.percentage').hide()
+ data.context.find('.exists_message').show()
+ $('#user_file_attachment').prop('disabled', false)
+ else
+ data.submit()
+ progress: (e, data) ->
+ if data.context
+ progress = parseInt(data.loaded / data.total * 100)
+ data.context.find('.percentage').html("#{progress}%")
+ if data.loaded == data.total
+ data.context.find('.spinner').hide()
+ data.context.find('.tick').show()
+ stop: (e) ->
+ folder = $('#target_folder_id').val()
+ window.location.href = "/folders/#{folder}"
diff --git a/app/assets/stylesheets/boxroom/application.scss b/app/assets/stylesheets/boxroom/application.scss
new file mode 100644
index 0000000..2d49a62
--- /dev/null
+++ b/app/assets/stylesheets/boxroom/application.scss
@@ -0,0 +1,88 @@
+/*
+*= require_self
+*= require_tree .
+*/
+
+html, body { height: 100%; }
+body { padding: 0px; margin: 0px; background-color: rgb(225,225,225); }
+body, input { font-family: Arial; font-size: 13pt; }
+input { padding: 3px; border: 1px solid rgb(210,210,210); }
+input[type='button'], input[type='submit'] { background-color: #EEE; padding: 5px; }
+input[type='file'] { border: 0; }
+textarea { border: 1px solid rgb(210,210,210); width: 700px; padding: 10px; font-family: Arial; font-size: 10pt; }
+img { border: 0; }
+h1 { font-size: 20pt; text-transform: uppercase; }
+h3 { font-size: 13pt; margin-bottom: 0; }
+a { color: #BA303A; text-decoration: none; }
+a:hover { text-decoration: underline; }
+table { border-collapse: collapse; }
+th { text-align: left; font-size: 11pt; }
+td img { padding: 0; }
+
+#container { min-height: 100%; position: relative; }
+#header { background-color: rgb(0,39,77); color: #FFF; border-bottom: 2px solid rgb(8,58,127); min-height: 70px; }
+#menu { background-color: rgb(0,24,49); border-bottom: 4px solid #BA303A; }
+#content { position: relative; width: 850px; margin: 10px auto 0 auto; padding: 15px 25px 15px 25px; background-color: #FFF; border-radius: 8px; border-right: 1px solid #CCC; border-bottom: 1px solid #AAA; }
+#footer { position: absolute; bottom: 0; width: 100%; background-color: rgb(0,24,49); color: #FFF; border-top: 4px solid #BA303A; }
+#footer_spacer { height: 70px; }
+
+#progress p span.status { margin-left: 3px; }
+#progress p img { vertical-align: middle; margin-bottom: 4px; }
+
+.menu { margin: 0; padding: 10px 15px 7px 15px; color: #FFF; }
+.menu a, .footer a { color: #FFF; }
+.footer { margin: 0; padding: 15px; text-align: center; }
+.footer a { text-decoration: underline; }
+.table_header td { font-weight: bold; }
+.even td, .odd td, th { padding: 12px 7px 7px 7px; }
+.even { background-color: #EEE; }
+.odd { background-color: #FFF; }
+.text_input { width: 280px; }
+.user_welcome { float: right; margin-right: 15px; }
+.user_welcome a { color: #FFF; text-decoration: underline; }
+.user_groups { margin-right: 200px; display: block; }
+.user_groups label { margin-right: 15px; }
+.user_name, .user_expiration { min-width: 180px; }
+.user_email { min-width: 370px; }
+.group_name, .clipboard_item { min-width: 483px; }
+.file_name { min-width: 250px; max-width: 400px; overflow: hidden; }
+.file_name a, .shared_file a { color: #000; text-decoration: underline; }
+.file_size { min-width: 100px; }
+.disabled { color: #999; }
+.breadcrumb { margin-bottom: 5px; padding: 4px; font-size: 11pt; background-color: #F6F6F6; border: 1px solid #DDD; border-bottom-color: #AAA; border-right-color: #CCC; display: inline-block; }
+.nowrap { white-space: nowrap; }
+.permission_column { width: 75px; text-align: center; }
+.permissions_button, .back { display: inline-block; margin-top: 15px; }
+.clipboard_empty { margin-top: 35px; }
+.clipboard_info_image { float: left; margin: 3px 15px 15px 0; }
+.button_to, .button_to div { display: inline; }
+.emails_to_share_with { height: 39px; width: 755px; }
+.share_message { height: 80px; width: 755px; }
+.shared_file { display: inline-block; min-width: 400px; padding: 15px; background-color: #EEE; }
+.shared_file img { margin-right: 8px; }
+.share_link_emails { font-size: 11pt; }
+.comma_seperated, .optional { position: relative; top: -1px; left: 3px; font-size: 10pt; }
+.char_counter { float: right; margin: 5px 75px 0 0; font-size: 10pt; }
+.translation .missing, .red { color: #F00; }
+
+.error, .notice { position: absolute; top: 0; left: 0; width: 100%; text-align: center; }
+.error { background-color: rgba(255,0,0,0.8); }
+.notice { background-color: rgba(0,165,0,0.7); }
+
+.error p, .notice p { display: inline-block; font-size: 16pt; color: #FFF; margin: 0; padding: 10px 0 10px 45px; background-repeat: no-repeat; background-position: 0 5px; }
+.error p { background-image: url(exclamation.png); }
+.notice p { background-image: url(information.png); }
+
+span.field_with_errors input, span.field_with_errors textarea { background-color: #FDD; }
+#errorExplanation { width: 500px; border: 2px solid #c00; padding: 7px 7px 15px 7px; margin: 10px 0 10px 0; background-color: #f0f0f0; }
+#errorExplanation p { padding-left: 8px; }
+#errorExplanation h2 { text-align: left; font-size: 14pt; font-weight: bold; padding: 5px 5px 5px 15px; margin: -7px; margin-bottom: 7px; background-color: #c00; color: #fff; }
+#errorExplanation ul { margin: 0; }
+#errorExplanation ul li { list-style: square; }
+
+.folder_menu, .user_menu, .group_menu { padding: 7px 14px 7px 14px; }
+.folder_menu a, .user_menu a, .group_menu a { outline: none; }
+
+.highlight { display: inline-block; margin: -7px 0 -7px 0; padding-left: 14px; background-color: #BA303A; border-radius: 15px; }
+.highlight span { display: inline-block; height: 27px; padding: 7px 14px 0 0; }
+.highlight a { text-decoration: none; color: #FFF; outline: none; }
diff --git a/app/controllers/boxroom/admins_controller.rb b/app/controllers/boxroom/admins_controller.rb
new file mode 100644
index 0000000..a8cb67a
--- /dev/null
+++ b/app/controllers/boxroom/admins_controller.rb
@@ -0,0 +1,28 @@
+module Boxroom
+ class AdminsController < ApplicationController
+ skip_before_action :require_admin_in_system, :require_login
+ before_action :require_no_admin
+
+ def new
+ @user = User.new
+ end
+
+ def create
+ @user = User.new(permitted_params.user)
+ @user.password_required = true
+ @user.is_admin = true
+
+ if @user.save
+ redirect_to new_session_url, :notice => t(:admin_user_created_successfully)
+ else
+ render :action => 'new'
+ end
+ end
+
+ private
+
+ def require_no_admin
+ redirect_to new_session_url unless User.no_admin_yet?
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/application_controller.rb b/app/controllers/boxroom/application_controller.rb
new file mode 100644
index 0000000..3a96328
--- /dev/null
+++ b/app/controllers/boxroom/application_controller.rb
@@ -0,0 +1,78 @@
+module Boxroom
+ class ApplicationController < ActionController::Base
+ protect_from_forgery
+
+ before_action :require_admin_in_system
+ before_action :require_login
+
+ helper_method :clipboard, :current_user, :signed_in?, :permitted_params
+
+ protected
+
+ def clipboard
+ session[:clipboard] ||= Clipboard.new
+ end
+
+ def current_user
+ @current_user ||= User.find_by_id(session[:user_id])
+ end
+
+ def signed_in?
+ !!current_user
+ end
+
+ def permitted_params
+ @permitted_params ||= PermittedParams.new(params, current_user)
+ end
+
+ def require_admin_in_system
+ redirect_to new_admin_url if User.no_admin_yet?
+ end
+
+ def require_admin
+ redirect_to :root unless current_user.member_of_admins?
+ end
+
+ def require_login
+ if current_user.nil?
+ user = User.find_by_remember_token(cookies[:auth_token]) unless cookies[:auth_token].blank?
+
+ if user.nil?
+ reset_session
+ session[:user_id] = nil
+ session[:return_to] = request.fullpath
+ redirect_to new_session_url
+ else
+ user.refresh_remember_token
+ session[:user_id] = user.id
+ cookies[:auth_token] = user.remember_token
+ end
+ end
+ end
+
+ def require_existing_target_folder
+ @target_folder = get_folder_or_redirect(params[:folder_id])
+ end
+
+ def require_create_permission
+ unless current_user.can_create(@target_folder)
+ redirect_to @target_folder, :alert => t(:no_permissions_for_this_type, :method => t(:create), :type => t(:this_folder))
+ end
+ end
+
+ %w{read update delete}.each do |method|
+ define_method "require_#{method}_permission" do
+ unless (method == 'read' && @folder.is_root?) || current_user.send("can_#{method}", @folder)
+ redirect_folder = @folder.parent.nil? ? Folder.root : @folder.parent
+ redirect_to redirect_folder, :alert => t(:no_permissions_for_this_type, :method => t(:create), :type => t(:this_folder))
+ end
+ end
+ end
+
+ def get_folder_or_redirect(id)
+ Folder.find(id)
+ rescue ActiveRecord::RecordNotFound
+ redirect_to Folder.root, :alert => t(:already_deleted, :type => t(:this_folder))
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/clipboard_controller.rb b/app/controllers/boxroom/clipboard_controller.rb
new file mode 100644
index 0000000..03ae974
--- /dev/null
+++ b/app/controllers/boxroom/clipboard_controller.rb
@@ -0,0 +1,77 @@
+module Boxroom
+ class ClipboardController < ApplicationController
+ before_action :require_existing_item, :except => :reset
+ before_action :require_existing_target_folder, :only => [:copy, :move]
+ before_action :require_target_is_not_child, :only => :move
+ before_action :require_create_permission, :only => [:copy, :move]
+ before_action :require_read_permission, :only => [:create, :copy, :move]
+ before_action :require_delete_permission, :only => :move
+
+ # @item is set in require_existing_item
+ def create
+ clipboard.add(@item)
+ redirect_to folder_url(params[:folder_id]), :notice => t(:added_to_clipboard)
+ end
+
+ # @item is set in require_existing_item
+ def destroy
+ clipboard.remove(@item)
+ redirect_to folder_url(params[:folder_id])
+ end
+
+ def reset
+ clipboard.reset
+ redirect_to folder_url(params[:folder_id])
+ end
+
+ def copy
+ paste :copy
+ end
+
+ def move
+ paste :move
+ end
+
+ private
+
+ # @item is set in require_existing_item
+ # @target_folder is set in require_existing_target_folder
+ def paste(action)
+ @item.send(action, @target_folder)
+ clipboard.remove(@item)
+ redirect_to folder_url(params[:folder_id])
+ rescue ActiveRecord::RecordInvalid
+ redirect_to folder_url(params[:folder_id]), :alert => t("could_not_#{action}", :type => t(params[:type]))
+ end
+
+ def require_existing_item
+ if params[:type] == 'folder'
+ @item = @folder = Folder.find(params[:id])
+ else
+ @item = UserFile.find(params[:id])
+ @folder = @item.folder
+ end
+ rescue ActiveRecord::RecordNotFound
+ redirect_to folder_url(params[:folder_id]), :alert => t(:already_deleted, :type => t("this_#{params[:type]}"))
+ end
+
+ def require_target_is_not_child
+ if params[:type] == 'folder'
+ if @folder == @target_folder || @folder.parent_of?(@target_folder)
+ redirect_to folder_url(params[:folder_id]), :alert => t(:cannot_move_to_own_subfolder)
+ end
+ end
+ end
+
+ # Overrides require_#{method}_permission in ApplicationController.
+ # Check if @folder can be read or deleted and redirects to the
+ # current folder (identified by params[:folder_id]) if not.
+ %w{read delete}.each do |method|
+ define_method "require_#{method}_permission" do
+ unless current_user.send("can_#{method}", @folder)
+ redirect_to folder_url(params[:folder_id]), :alert => t(:no_permissions_for_this_type, :method => t(method), :type => t("this_#{params[:type]}"))
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/files_controller.rb b/app/controllers/boxroom/files_controller.rb
new file mode 100644
index 0000000..fafb513
--- /dev/null
+++ b/app/controllers/boxroom/files_controller.rb
@@ -0,0 +1,64 @@
+module Boxroom
+ class FilesController < ApplicationController
+ before_action :require_existing_file, :only => [:show, :edit, :update, :destroy]
+ before_action :require_existing_target_folder, :only => [:new, :create]
+
+ before_action :require_create_permission, :only => [:new, :create]
+ before_action :require_read_permission, :only => :show
+ before_action :require_update_permission, :only => [:edit, :update]
+ before_action :require_delete_permission, :only => :destroy
+
+ # @file and @folder are set in require_existing_file
+ def show
+ send_file @file.attachment.path, :filename => @file.attachment_file_name
+ end
+
+ # @target_folder is set in require_existing_target_folder
+ def new
+ @file = @target_folder.user_files.build
+ end
+
+ # @target_folder is set in require_existing_target_folder
+ def create
+ @file = @target_folder.user_files.create(permitted_params.user_file)
+ render :nothing => true
+ end
+
+ # @file and @folder are set in require_existing_file
+ def edit
+ end
+
+ # @file and @folder are set in require_existing_file
+ def update
+ if @file.update_attributes(permitted_params.user_file)
+ redirect_to edit_file_url(@file), :notice => t(:your_changes_were_saved)
+ else
+ render :action => 'edit'
+ end
+ end
+
+ # @file and @folder are set in require_existing_file
+ def destroy
+ @file.destroy
+ redirect_to @folder
+ end
+
+ def exists
+ @folder = Folder.find(params[:folder])
+
+ if current_user.can_read(@folder) || current_user.can_write(@folder)
+ @file = @folder.user_files.build(:attachment_file_name => params[:name].gsub(RESTRICTED_CHARACTERS, '_'))
+ render :json => !@file.valid?
+ end
+ end
+
+ private
+
+ def require_existing_file
+ @file = UserFile.find(params[:id])
+ @folder = @file.folder
+ rescue ActiveRecord::RecordNotFound
+ redirect_to Folder.root, :alert => t(:already_deleted, :type => t(:this_file))
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/folders_controller.rb b/app/controllers/boxroom/folders_controller.rb
new file mode 100644
index 0000000..7587a3e
--- /dev/null
+++ b/app/controllers/boxroom/folders_controller.rb
@@ -0,0 +1,89 @@
+module Boxroom
+ class FoldersController < ApplicationController
+ before_action :require_existing_folder, :only => [:show, :edit, :update, :destroy]
+ before_action :require_existing_target_folder, :only => [:new, :create]
+ before_action :require_folder_isnt_root_folder, :only => [:edit, :update, :destroy]
+
+ before_action :require_create_permission, :only => [:new, :create]
+ before_action :require_read_permission, :only => :show
+ before_action :require_update_permission, :only => [:edit, :update]
+ before_action :require_delete_permission, :only => :destroy
+
+ def index
+ redirect_to Folder.root
+ end
+
+ # Note: @folder is set in require_existing_folder
+ def show
+ end
+
+ # Note: @target_folder is set in require_existing_target_folder
+ def new
+ @folder = @target_folder.children.build
+ end
+
+ # Note: @target_folder is set in require_existing_target_folder
+ def create
+ @folder = @target_folder.children.build(permitted_params.folder)
+
+ if @folder.save
+ redirect_to @target_folder
+ else
+ render :action => 'new'
+ end
+ end
+
+ # Note: @folder is set in require_existing_folder
+ def edit
+ end
+
+ # Note: @folder is set in require_existing_folder
+ def update
+ if @folder.update_attributes(permitted_params.folder)
+ redirect_to edit_folder_url(@folder), :notice => t(:your_changes_were_saved)
+ else
+ render :action => 'edit'
+ end
+ end
+
+ # Note: @folder is set in require_existing_folder
+ def destroy
+ target_folder = @folder.parent
+ @folder.destroy
+ redirect_to target_folder
+ end
+
+ private
+
+ # get_folder_or_redirect is defined in ApplicationController
+ def require_existing_folder
+ @folder = get_folder_or_redirect(params[:id])
+ end
+
+ def require_folder_isnt_root_folder
+ if @folder.is_root?
+ redirect_to Folder.root, :alert => t(:cannot_delete_root_folder)
+ end
+ end
+
+ # Overrides require_delete_permission in ApplicationController
+ def require_delete_permission
+ unless @folder.is_root? || current_user.can_delete(@folder)
+ redirect_to @folder.parent, :alert => t(:no_permissions_for_this_type, :method => t(:delete), :type => t(:this_folder))
+ else
+ require_delete_permissions_for(@folder.children)
+ end
+ end
+
+ def require_delete_permissions_for(folders)
+ folders.each do |folder|
+ unless current_user.can_delete(folder)
+ redirect_to @folder.parent, :alert => t(:no_delete_permissions_for_subfolder)
+ else
+ # Recursive...
+ require_delete_permissions_for(folder.children)
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/groups_controller.rb b/app/controllers/boxroom/groups_controller.rb
new file mode 100644
index 0000000..de30dda
--- /dev/null
+++ b/app/controllers/boxroom/groups_controller.rb
@@ -0,0 +1,58 @@
+module Boxroom
+ class GroupsController < ApplicationController
+ before_action :require_admin
+ before_action :require_existing_group, :only => [:edit, :update, :destroy]
+ before_action :require_group_isnt_admins_group, :only => [:edit, :update, :destroy]
+
+ def index
+ @groups = Group.order(:name)
+ end
+
+ def new
+ @group = Group.new
+ end
+
+ def create
+ @group = Group.new(permitted_params.group)
+
+ if @group.save
+ redirect_to groups_url
+ else
+ render :action => 'new'
+ end
+ end
+
+ # Note: @group is set in require_existing_group
+ def edit
+ end
+
+ # Note: @group is set in require_existing_group
+ def update
+ if @group.update_attributes(permitted_params.group)
+ redirect_to edit_group_url(@group), :notice => t(:your_changes_were_saved)
+ else
+ render :action => 'edit'
+ end
+ end
+
+ # Note: @group is set in require_existing_group
+ def destroy
+ @group.destroy
+ redirect_to groups_url
+ end
+
+ private
+
+ def require_existing_group
+ @group = Group.find(params[:id])
+ rescue ActiveRecord::RecordNotFound
+ redirect_to groups_url, :alert => t(:group_already_deleted)
+ end
+
+ def require_group_isnt_admins_group
+ if @group.admins_group?
+ redirect_to groups_url, :alert => t(:admins_group_cannot_be_deleted)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/permissions_controller.rb b/app/controllers/boxroom/permissions_controller.rb
new file mode 100644
index 0000000..720f10e
--- /dev/null
+++ b/app/controllers/boxroom/permissions_controller.rb
@@ -0,0 +1,17 @@
+module Boxroom
+ class PermissionsController < ApplicationController
+ before_action :require_admin
+
+ def update_multiple
+ if params[:permissions]
+ permissions = Permission.update(params[:permissions].keys, params[:permissions].values)
+ folder = permissions.first.folder
+ folder.copy_permissions_to_children(permissions) if params[:recursive] && folder.has_children?
+ end
+
+ redirect_to :back
+ rescue ActiveRecord::RecordNotFound # Folder was deleted, so permissions are gone too
+ redirect_to Folder.root, :alert => t(:already_deleted, :type => t(:this_folder))
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/reset_password_controller.rb b/app/controllers/boxroom/reset_password_controller.rb
new file mode 100644
index 0000000..e916077
--- /dev/null
+++ b/app/controllers/boxroom/reset_password_controller.rb
@@ -0,0 +1,43 @@
+module Boxroom
+ class ResetPasswordController < ApplicationController
+ before_action :require_valid_token, :only => [:edit, :update]
+ skip_before_action :require_login
+
+ def new
+ end
+
+ def create
+ user = User.find_by_email(params[:email])
+
+ unless user.nil?
+ user.refresh_reset_password_token
+ UserMailer.reset_password_email(user).deliver_now
+ end
+
+ redirect_to new_reset_password_url, :notice => t(:instruction_email_sent, :email => params[:email])
+ end
+
+ # Note: @user is set in require_valid_token
+ def edit
+ end
+
+ # Note: @user is set in require_valid_token
+ def update
+ if @user.update_attributes(permitted_params.user.merge({:password_required => true}))
+ redirect_to new_session_url, :notice => t(:password_reset_successfully)
+ else
+ render :action => 'edit'
+ end
+ end
+
+ private
+
+ def require_valid_token
+ @user = User.find_by_reset_password_token(params[:id])
+
+ if @user.nil? || @user.reset_password_token_expires_at < Time.now
+ redirect_to new_reset_password_url, :alert => t(:reset_url_expired)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/sessions_controller.rb b/app/controllers/boxroom/sessions_controller.rb
new file mode 100644
index 0000000..12cbbe9
--- /dev/null
+++ b/app/controllers/boxroom/sessions_controller.rb
@@ -0,0 +1,46 @@
+module Boxroom
+ class SessionsController < ApplicationController
+ skip_before_action :require_login
+
+ def new
+ end
+
+ def create
+ user = User.authenticate(params[:username], params[:password])
+
+ unless user.nil?
+ if params[:remember_me] == 'true'
+ user.refresh_remember_token
+ cookies[:auth_token] = {:value => user.remember_token, :expires => 2.weeks.from_now}
+ end
+
+ session[:user_id] = user.id
+ redirect_url = session.delete(:return_to) || folders_url
+ redirect_to redirect_url, :only_path => true
+ else
+ log_failed_sign_in_attempt(Time.now, params[:username], request.remote_ip)
+ redirect_to new_session_url, :alert => t(:credentials_incorrect)
+ end
+ end
+
+ def destroy
+ current_user.forget_me
+ cookies.delete :auth_token
+ reset_session
+ session[:user_id] = nil
+ redirect_to new_session_url
+ end
+
+ private
+
+ def log_failed_sign_in_attempt(date, username, ip)
+ Rails.logger.error(
+ "\nFAILED SIGN IN ATTEMPT:\n" +
+ "=======================\n" +
+ " Date: #{date}\n" +
+ " Username: #{username}\n" +
+ " IP address: #{ip}\n\n"
+ )
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/share_links_controller.rb b/app/controllers/boxroom/share_links_controller.rb
new file mode 100644
index 0000000..71696f4
--- /dev/null
+++ b/app/controllers/boxroom/share_links_controller.rb
@@ -0,0 +1,65 @@
+module Boxroom
+ class ShareLinksController < ApplicationController
+ before_action :require_admin, :only => [:index, :destroy]
+ before_action :require_existing_file, :except => [:index, :destroy]
+ before_action :require_existing_share_link, :only => :destroy
+ before_action :require_read_permission, :only => [:new, :create]
+ skip_before_action :require_login, :only => :show
+
+ rescue_from ActiveRecord::RecordNotFound, NoMethodError, RuntimeError, :with => :redirect_to_root_or_signin_and_show_alert
+
+ def index
+ @share_links = ShareLink.active_share_links
+ end
+
+ # Note: @file is set in require_existing_file
+ def show
+ send_file @file.attachment.path, :filename => @file.attachment_file_name unless @file.nil?
+ end
+
+ # Note: @file is set in require_existing_file
+ def new
+ @share_link = @file.share_links.build
+ end
+
+ # Note: @file and @folder are set in require_existing_file
+ def create
+ @share_link = @file.share_links.build(permitted_params.share_link)
+ @share_link.user = current_user
+
+ if @share_link.save
+ UserMailer.share_link_email(@share_link).deliver_now
+ redirect_to @folder, :notice => t(:shared_successfully)
+ else
+ render :action => 'new'
+ end
+ end
+
+ # Note: @share_link is set in require_existing_share_link
+ def destroy
+ @share_link.destroy
+ redirect_to share_links_url
+ end
+
+ private
+
+ def require_existing_file
+ @file = params[:file_id].blank? ? ShareLink.file_for_token(params[:id]) : UserFile.find(params[:file_id])
+ @folder = @file.folder
+ end
+
+ def require_existing_share_link
+ @share_link = ShareLink.find(params[:id])
+ rescue ActiveRecord::RecordNotFound
+ redirect_to share_links_url, :alert => t(:already_deleted, :type => t(:this_share_link))
+ end
+
+ def redirect_to_root_or_signin_and_show_alert
+ if signed_in?
+ redirect_to Folder.root, :alert => t(:already_deleted, :type => t(:this_file))
+ else
+ redirect_to signin_url, :alert => t(:already_deleted, :type => t(:this_file))
+ end
+ end
+ end
+end
diff --git a/app/controllers/boxroom/signup_controller.rb b/app/controllers/boxroom/signup_controller.rb
new file mode 100644
index 0000000..72dc191
--- /dev/null
+++ b/app/controllers/boxroom/signup_controller.rb
@@ -0,0 +1,29 @@
+module Boxroom
+ class SignupController < ApplicationController
+ before_action :require_valid_token, :only => [:edit, :update]
+ skip_before_action :require_login
+
+ # Note: @user is set in require_valid_token
+ def edit
+ end
+
+ # Note: @user is set in require_valid_token
+ def update
+ if @user.update_attributes(permitted_params.user.merge({:password_required => true}))
+ redirect_to new_session_url, :notice => t(:signed_up_successfully)
+ else
+ render :action => 'edit'
+ end
+ end
+
+ private
+
+ def require_valid_token
+ @user = User.find_by_signup_token(params[:id])
+
+ if @user.nil? || @user.signup_token_expires_at < Time.now
+ redirect_to new_session_url, :alert => t(:sign_url_expired)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/boxroom/users_controller.rb b/app/controllers/boxroom/users_controller.rb
new file mode 100644
index 0000000..eab1faa
--- /dev/null
+++ b/app/controllers/boxroom/users_controller.rb
@@ -0,0 +1,73 @@
+module Boxroom
+ class UsersController < ApplicationController
+ before_action :require_admin, :except => [:edit, :update]
+ before_action :require_existing_user, :only => [:edit, :update, :destroy, :extend]
+ before_action :require_deleted_user_isnt_admin, :only => :destroy
+
+ def index
+ @users = User.where.not(:name => nil).order('name')
+ @new_users = User.where(:name => nil).order('email')
+ end
+
+ def new
+ @user = User.new
+ end
+
+ def create
+ @user = User.new(permitted_params.user)
+
+ if @user.save
+ UserMailer.signup_email(@user).deliver_now
+ redirect_to users_url
+ else
+ render :action => 'new'
+ end
+ end
+
+ # Note: @user is set in require_existing_user
+ def edit
+ end
+
+ # Note: @user is set in require_existing_user
+ def update
+ if @user.update_attributes(permitted_params.user.merge({:password_required => false}))
+ redirect_to edit_user_url(@user), :notice => t(:your_changes_were_saved)
+ else
+ render :action => 'edit'
+ end
+ end
+
+ # Note: @user is set in require_existing_user
+ def extend
+ @user.signup_token_expires_at = @user.signup_token_expires_at + 2.weeks
+ @user.save(:validate => false)
+ redirect_to users_url
+ end
+
+ # Note: @user is set in require_existing_user
+ def destroy
+ @user.destroy
+ redirect_to users_url
+ end
+
+ private
+
+ def require_existing_user
+ if current_user.member_of_admins? && params[:id] != current_user.id.to_s
+ @title = t(:edit_user)
+ @user = User.find(params[:id])
+ else
+ @title = t(:account_settings)
+ @user = current_user
+ end
+ rescue ActiveRecord::RecordNotFound
+ redirect_to users_url, :alert => t(:user_already_deleted)
+ end
+
+ def require_deleted_user_isnt_admin
+ if @user.is_admin
+ redirect_to users_url, :alert => t(:admin_user_cannot_be_deleted)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/helpers/boxroom/application_helper.rb b/app/helpers/boxroom/application_helper.rb
new file mode 100644
index 0000000..1eb2a05
--- /dev/null
+++ b/app/helpers/boxroom/application_helper.rb
@@ -0,0 +1,4 @@
+module Boxroom
+ module ApplicationHelper
+ end
+end
diff --git a/app/helpers/boxroom/folders_helper.rb b/app/helpers/boxroom/folders_helper.rb
new file mode 100644
index 0000000..8c22558
--- /dev/null
+++ b/app/helpers/boxroom/folders_helper.rb
@@ -0,0 +1,17 @@
+module Boxroom
+ module FoldersHelper
+ def breadcrumbs(folder, breadcrumbs = '')
+ breadcrumbs = "#{link_to(folder.parent.name, folder.parent)} » #{breadcrumbs}"
+ breadcrumbs = breadcrumbs(folder.parent, breadcrumbs) unless folder.parent == Folder.root
+ breadcrumbs.html_safe
+ end
+
+ def file_icon(extension)
+ if extension && FileTest.exists?(Rails.root.join('app', 'assets', 'images', 'fileicons', "#{extension.downcase}.png"))
+ "fileicons/#{extension.downcase}.png"
+ else
+ 'file.png'
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/jobs/boxroom/application_job.rb b/app/jobs/boxroom/application_job.rb
new file mode 100644
index 0000000..4e5aa9c
--- /dev/null
+++ b/app/jobs/boxroom/application_job.rb
@@ -0,0 +1,4 @@
+module Boxroom
+ class ApplicationJob < ActiveJob::Base
+ end
+end
diff --git a/app/mailers/boxroom/application_mailer.rb b/app/mailers/boxroom/application_mailer.rb
new file mode 100644
index 0000000..aa23025
--- /dev/null
+++ b/app/mailers/boxroom/application_mailer.rb
@@ -0,0 +1,6 @@
+module Boxroom
+ class ApplicationMailer < ActionMailer::Base
+ default from: 'from@example.com'
+ layout 'mailer'
+ end
+end
diff --git a/app/mailers/boxroom/user_mailer.rb b/app/mailers/boxroom/user_mailer.rb
new file mode 100644
index 0000000..1f701ac
--- /dev/null
+++ b/app/mailers/boxroom/user_mailer.rb
@@ -0,0 +1,18 @@
+module Boxroom
+ class UserMailer < ActionMailer::Base
+ def signup_email(user)
+ @user = user
+ mail(:to => user.email, :subject => t(:signup_email_subject))
+ end
+
+ def reset_password_email(user)
+ @user = user
+ mail(:to => user.email, :subject => t(:reset_password_email_subject))
+ end
+
+ def share_link_email(share_link)
+ @share_link = share_link
+ mail(:to => share_link.user.email, :reply_to => share_link.user.email, :bcc => share_link.emails, :subject => t(:share_link_email_subject, :email => share_link.user.email))
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/models/boxroom/application_record.rb b/app/models/boxroom/application_record.rb
new file mode 100644
index 0000000..6b43fef
--- /dev/null
+++ b/app/models/boxroom/application_record.rb
@@ -0,0 +1,5 @@
+module Boxroom
+ class ApplicationRecord < ActiveRecord::Base
+ self.abstract_class = true
+ end
+end
diff --git a/app/models/boxroom/clipboard.rb b/app/models/boxroom/clipboard.rb
new file mode 100644
index 0000000..d61fa0d
--- /dev/null
+++ b/app/models/boxroom/clipboard.rb
@@ -0,0 +1,45 @@
+module Boxroom
+ class Clipboard
+ def initialize
+ setup
+ end
+
+ def folders
+ Folder.where(:id => @folders)
+ end
+
+ def files
+ UserFile.where(:id => @files)
+ end
+
+ def add(item)
+ if item.class == Folder
+ @folders << item.id unless @folders.include?(item.id)
+ else
+ @files << item.id unless @files.include?(item.id)
+ end
+ end
+
+ def remove(item)
+ if item.class == Folder
+ @folders.delete(item.id)
+ else
+ @files.delete(item.id)
+ end
+ end
+
+ def empty?
+ (@folders.empty? || folders.empty?) && (@files.empty? || files.empty?)
+ end
+
+ def reset
+ setup
+ end
+
+ private
+
+ def setup
+ @folders, @files = [], []
+ end
+ end
+end
diff --git a/app/models/boxroom/folder.rb b/app/models/boxroom/folder.rb
new file mode 100644
index 0000000..fc32473
--- /dev/null
+++ b/app/models/boxroom/folder.rb
@@ -0,0 +1,113 @@
+module Boxroom
+ class Folder < ActiveRecord::Base
+ acts_as_tree :order => 'name'
+
+ has_many :user_files, -> {order :attachment_file_name}, :dependent => :destroy
+ has_many :permissions, :dependent => :destroy
+
+ attr_accessor :is_copied_folder
+
+ validates_uniqueness_of :name, :scope => :parent_id
+ validates_presence_of :name
+
+ before_save :check_for_parent
+ after_create :create_permissions, :unless => :is_copied_folder
+ before_destroy :dont_destroy_root_folder
+
+ def copy(target_folder, originally_copied_folder = nil)
+ new_folder = self.dup
+ new_folder.is_copied_folder = true
+ new_folder.parent = target_folder
+ new_folder.save!
+
+ originally_copied_folder = new_folder if originally_copied_folder.nil?
+
+ # Copy original folder's permissions
+ self.permissions.each do |permission|
+ new_permission = permission.dup
+ new_permission.folder = new_folder
+ new_permission.save!
+ end
+
+ self.user_files.each do |file|
+ file.copy(new_folder)
+ end
+
+ # Copy sub-folders recursively
+ self.children.each do |folder|
+ folder.copy(new_folder, originally_copied_folder) unless folder == originally_copied_folder
+ end
+
+ new_folder
+ end
+
+ def move(target_folder)
+ unless target_folder == self || self.parent_of?(target_folder)
+ self.parent = target_folder
+ save!
+ else
+ raise 'You cannot move a folder to its own sub-folder.'
+ end
+ end
+
+ def copy_permissions_to_children(permissions_to_copy)
+ permissions_to_copy.each do |permission|
+ attributes = permission.attributes.except('id', 'folder_id', 'group_id')
+ Permission.where(:folder_id => children, :group_id => permission.group_id).update_all(attributes)
+ end
+
+ # Copy permissions recursively
+ children.each do |child|
+ child.copy_permissions_to_children(permissions_to_copy) if child.has_children?
+ end
+ end
+
+ def parent_of?(folder)
+ self.children.each do |child|
+ if child == folder
+ return true
+ else
+ return child.parent_of?(folder)
+ end
+ end
+ false
+ end
+
+ def is_root?
+ parent.nil? && !new_record?
+ end
+
+ def has_children?
+ children.count > 0
+ end
+
+ def self.root
+ @root_folder ||= find_by_name_and_parent_id('Root folder', nil)
+ end
+
+ private
+
+ def check_for_parent
+ raise 'Folders must have a parent.' if parent.nil? && name != 'Root folder'
+ end
+
+ def create_permissions
+ unless is_root?
+ parent.permissions.each do |permission|
+ Permission.create! do |p|
+ p.group = permission.group
+ p.folder = self
+ p.can_create = permission.can_create
+ p.can_read = permission.can_read
+ p.can_update = permission.can_update
+ p.can_delete = permission.can_delete
+ end
+ end
+ end
+ end
+
+ def dont_destroy_root_folder
+ raise "Can't delete Root folder" if is_root?
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/models/boxroom/group.rb b/app/models/boxroom/group.rb
new file mode 100644
index 0000000..b3ce6c5
--- /dev/null
+++ b/app/models/boxroom/group.rb
@@ -0,0 +1,57 @@
+module Boxroom
+ class Group < ActiveRecord::Base
+ has_many :permissions, :dependent => :destroy
+ has_and_belongs_to_many :users
+
+ validates_uniqueness_of :name
+ validates_presence_of :name
+
+ after_create :create_admin_permissions, :if => :admins_group?
+ after_create :create_permissions, :unless => :admins_group?
+ before_destroy :dont_destroy_admins
+
+ def admins_group?
+ name == 'Admins'
+ end
+
+ def self.admins_group
+ where(:name => 'Admins').first
+ end
+
+ def self.all_except_admins
+ where.not(:name => 'Admins')
+ end
+
+ private
+
+ def create_admin_permissions
+ Folder.find_each do |folder|
+ Permission.create! do |p|
+ p.group = self
+ p.folder = folder
+ p.can_create = true
+ p.can_read = true
+ p.can_update = true
+ p.can_delete = true
+ end
+ end
+ end
+
+ def create_permissions
+ Folder.find_each do |folder|
+ Permission.create! do |p|
+ p.group = self
+ p.folder = folder
+ p.can_create = false
+ p.can_read = folder.is_root? # New groups can read the root folder
+ p.can_update = false
+ p.can_delete = false
+ end
+ end
+ end
+
+ def dont_destroy_admins
+ raise "Can't delete admins group" if admins_group?
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/models/boxroom/permission.rb b/app/models/boxroom/permission.rb
new file mode 100644
index 0000000..b02a89c
--- /dev/null
+++ b/app/models/boxroom/permission.rb
@@ -0,0 +1,6 @@
+module Boxroom
+ class Permission < ActiveRecord::Base
+ belongs_to :group
+ belongs_to :folder
+ end
+end
\ No newline at end of file
diff --git a/app/models/boxroom/permitted_params.rb b/app/models/boxroom/permitted_params.rb
new file mode 100644
index 0000000..0643796
--- /dev/null
+++ b/app/models/boxroom/permitted_params.rb
@@ -0,0 +1,33 @@
+module Boxroom
+ class PermittedParams < Struct.new(:params, :current_user)
+ %w{folder group share_link user user_file}.each do |model_name|
+ define_method model_name do
+ params.require(model_name.to_sym).permit(*send("#{model_name}_attributes"))
+ end
+ end
+
+ def folder_attributes
+ [:name]
+ end
+
+ def group_attributes
+ [:name]
+ end
+
+ def share_link_attributes
+ [:emails, :link_expires_at, :message]
+ end
+
+ def user_attributes
+ if current_user && current_user.member_of_admins?
+ [:name, :email, :password, :password_confirmation, {:group_ids => []}]
+ else
+ [:name, :email, :password, :password_confirmation]
+ end
+ end
+
+ def user_file_attributes
+ [:attachment, :attachment_file_name]
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/models/boxroom/share_link.rb b/app/models/boxroom/share_link.rb
new file mode 100644
index 0000000..86c27d5
--- /dev/null
+++ b/app/models/boxroom/share_link.rb
@@ -0,0 +1,40 @@
+module Boxroom
+ class ShareLink < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :user_file
+
+ validates_presence_of :emails, :link_expires_at
+ validates_length_of :emails, :maximum => 255
+ validate :format_of_emails
+
+ before_save :generate_token
+
+ def self.active_share_links
+ where('link_expires_at >= ?', DateTime.now).order(:link_expires_at)
+ end
+
+ def self.file_for_token(token)
+ share_link = find_by_link_token(token)
+
+ if share_link.link_expires_at < DateTime.now
+ raise 'This share link expired.'
+ else
+ share_link.user_file
+ end
+ end
+
+ private
+
+ def format_of_emails
+ emails.split(/,\s*/).each do |email|
+ unless email.strip =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/
+ errors.add(:emails, I18n.t(:are_invalid_due_to, :email => email))
+ end
+ end
+ end
+
+ def generate_token
+ self.link_token = SecureRandom.hex(10)
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/models/boxroom/user.rb b/app/models/boxroom/user.rb
new file mode 100644
index 0000000..c98bd3a
--- /dev/null
+++ b/app/models/boxroom/user.rb
@@ -0,0 +1,113 @@
+module Boxroom
+ class User < ActiveRecord::Base
+ has_and_belongs_to_many :groups
+ has_many :share_links
+
+ attr_accessor :password_confirmation, :password_required, :dont_clear_reset_password_token
+
+ validates_confirmation_of :password
+ validates_length_of :password, :in => 6..20, :allow_blank => true
+ validates_presence_of :password, :if => :password_required
+ validates_presence_of :name, :unless => :new_record?
+ validates_presence_of :email
+ validates_uniqueness_of :name, :unless => :new_record? && :name_is_blank?
+ validates_uniqueness_of :email
+ validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/
+
+ before_create :set_signup_token
+ before_save :clear_reset_password_token, :unless => :dont_clear_reset_password_token
+ before_update :clear_signup_token
+ after_create :create_root_folder_and_admins_group, :if => :is_admin
+ before_destroy :dont_destroy_admin
+
+ %w{create read update delete}.each do |method|
+ define_method "can_#{method}" do |folder|
+ has_permission = false
+
+ Permission.where(:group_id => groups, :folder_id => folder.id).each do |permission|
+ has_permission = permission.send("can_#{method}")
+ break if has_permission
+ end
+
+ has_permission
+ end
+ end
+
+ def password
+ @password
+ end
+
+ def password=(new_password)
+ @password = new_password
+
+ unless @password.blank?
+ self.password_salt = SecureRandom.base64(32)
+ self.hashed_password = Digest::SHA256.hexdigest(password_salt + password)
+ end
+ end
+
+ def member_of_admins?
+ groups.admins_group.present?
+ end
+
+ def refresh_reset_password_token
+ self.reset_password_token = SecureRandom.hex(10)
+ self.reset_password_token_expires_at = 1.hour.from_now
+ self.dont_clear_reset_password_token = true
+ save(:validate => false)
+ end
+
+ def refresh_remember_token
+ self.remember_token = SecureRandom.base64(32)
+ save(:validate => false)
+ end
+
+ def forget_me
+ self.remember_token = nil
+ save(:validate => false)
+ end
+
+ def name_is_blank?
+ self.name.blank?
+ end
+
+ def self.authenticate(name, password)
+ return nil if name.blank? || password.blank?
+ user = find_by_name(name) or return nil
+ hash = Digest::SHA256.hexdigest(user.password_salt + password)
+ hash == user.hashed_password ? user : nil
+ end
+
+ def self.no_admin_yet?
+ find_by_is_admin(true).blank?
+ end
+
+ private
+
+ def set_signup_token
+ self.signup_token = SecureRandom.hex(10)
+ self.signup_token_expires_at = 2.weeks.from_now
+ end
+
+ def clear_signup_token
+ unless self.name.blank?
+ self.signup_token = nil
+ self.signup_token_expires_at = nil
+ end
+ end
+
+ def clear_reset_password_token
+ self.reset_password_token = nil
+ self.reset_password_token_expires_at = nil
+ end
+
+ def create_root_folder_and_admins_group
+ Folder.create(:name => 'Root folder')
+ groups << Group.create(:name => 'Admins')
+ end
+
+ def dont_destroy_admin
+ raise "Can't delete original admin user" if is_admin
+ end
+ end
+end
diff --git a/app/models/boxroom/user_file.rb b/app/models/boxroom/user_file.rb
new file mode 100644
index 0000000..5673a30
--- /dev/null
+++ b/app/models/boxroom/user_file.rb
@@ -0,0 +1,35 @@
+module Boxroom
+ class UserFile < ActiveRecord::Base
+ has_attached_file :attachment, :path => ':rails_root/uploads/:rails_env/:id/:style/:id', :restricted_characters => RESTRICTED_CHARACTERS
+ do_not_validate_attachment_file_type :attachment
+
+ belongs_to :folder
+ has_many :share_links, :dependent => :destroy
+
+ validates_attachment_presence :attachment, :message => I18n.t(:blank, :scope => [:activerecord, :errors, :messages])
+ validates_presence_of :folder_id
+ validates_uniqueness_of :attachment_file_name, :scope => 'folder_id', :message => I18n.t(:exists_already, :scope => [:activerecord, :errors, :messages])
+ validates_format_of :attachment_file_name, :with => /\A[^\/\\\?\*:|"<>]+\z/, :message => I18n.t(:invalid_characters, :scope => [:activerecord, :errors, :messages])
+
+ def copy(target_folder)
+ new_file = self.dup
+ new_file.folder = target_folder
+ new_file.save!
+
+ path = "#{Rails.root}/uploads/#{Rails.env}/#{new_file.id}/original"
+ FileUtils.mkdir_p path
+ FileUtils.cp_r self.attachment.path, "#{path}/#{new_file.id}"
+
+ new_file
+ end
+
+ def move(target_folder)
+ self.folder = target_folder
+ save!
+ end
+
+ def extension
+ File.extname(attachment_file_name)[1..-1]
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/views/boxroom/admins/new.html.erb b/app/views/boxroom/admins/new.html.erb
new file mode 100644
index 0000000..5da81cf
--- /dev/null
+++ b/app/views/boxroom/admins/new.html.erb
@@ -0,0 +1,28 @@
+<% content_for :title, t(:create_admin) -%>
+
+
<%= content_for :title %>
+
+ <%= t :no_administrator_yet %>
+
+<%= form_for @user, :url => { :action => 'create' } do |f| %>
+ <%= f.error_messages %>
+
+ <%= f.label :name, t(:username) %>:
+ <%= f.text_field :name, { :class => 'text_input' } %>
+
+
+ <%= f.label :email %>:
+ <%= f.text_field :email, { :class => 'text_input' } %>
+
+
+ <%= label_tag :password %>:
+ <%= f.password_field :password, { :class => 'text_input' } %>
+
+
+ <%= label_tag :confirm_password %>:
+ <%= f.password_field :password_confirmation, { :class => 'text_input' } %>
+
+
+ <%= f.submit t(:create_admin_account) %>
+
+<% end %>
diff --git a/app/views/boxroom/clipboard/_clipboard_empty.de.html.erb b/app/views/boxroom/clipboard/_clipboard_empty.de.html.erb
new file mode 100644
index 0000000..9c1f7e9
--- /dev/null
+++ b/app/views/boxroom/clipboard/_clipboard_empty.de.html.erb
@@ -0,0 +1,2 @@
+Die Zwischenablage ist leer
+Gehen Sie <%= link_to 'zurück', '#', :class => 'back_link' %> und benutzen Sie die Funktion Zwischenablage, um Ordner oder Dateien in die Zwischenablage zu kopieren.
diff --git a/app/views/boxroom/clipboard/_clipboard_empty.en.html.erb b/app/views/boxroom/clipboard/_clipboard_empty.en.html.erb
new file mode 100644
index 0000000..7bc93b4
--- /dev/null
+++ b/app/views/boxroom/clipboard/_clipboard_empty.en.html.erb
@@ -0,0 +1,2 @@
+The clipboard is empty
+Go <%= link_to 'back', '#', :class => 'back_link' %> and use the clipboard button to add files or folders to the clipboard.
diff --git a/app/views/boxroom/clipboard/_clipboard_empty.es.html.erb b/app/views/boxroom/clipboard/_clipboard_empty.es.html.erb
new file mode 100644
index 0000000..b55e6e1
--- /dev/null
+++ b/app/views/boxroom/clipboard/_clipboard_empty.es.html.erb
@@ -0,0 +1,2 @@
+El portapapeles esta vacío
+Volver <%= link_to 'back', '#', :class => 'back_link' %> para usar el boton de portapapeles y agregar archivos o carpetas al portapapeles.
diff --git a/app/views/boxroom/clipboard/_clipboard_empty.fr.html.erb b/app/views/boxroom/clipboard/_clipboard_empty.fr.html.erb
new file mode 100644
index 0000000..725bb06
--- /dev/null
+++ b/app/views/boxroom/clipboard/_clipboard_empty.fr.html.erb
@@ -0,0 +1,2 @@
+Le presse-papier est vide !
+Revenir <%= link_to 'en arrière', '#', :class => 'back_link' %> et utilisez le bouton "presse-papier" pour y ajouter des fichiers ou dossiers.
diff --git a/app/views/boxroom/clipboard/_clipboard_empty.it.html.erb b/app/views/boxroom/clipboard/_clipboard_empty.it.html.erb
new file mode 100644
index 0000000..90a8da3
--- /dev/null
+++ b/app/views/boxroom/clipboard/_clipboard_empty.it.html.erb
@@ -0,0 +1,2 @@
+Nessun file presente negli appunti
+Vai <%= link_to 'indietro', '#', :class => 'back_link' %> e usa il pulsante appunti per aggiungere file o cartelle agli appunti.
diff --git a/app/views/boxroom/clipboard/_clipboard_empty.nl.html.erb b/app/views/boxroom/clipboard/_clipboard_empty.nl.html.erb
new file mode 100644
index 0000000..422bda9
--- /dev/null
+++ b/app/views/boxroom/clipboard/_clipboard_empty.nl.html.erb
@@ -0,0 +1,2 @@
+Het klembord is leeg
+Ga <%= link_to 'terug', '#', :class => 'back_link' %> en gebruik de klembord knop om bestanden en mappen toe te voegen aan het klembord.
diff --git a/app/views/boxroom/clipboard/_clipboard_empty.zh-CN.html.erb b/app/views/boxroom/clipboard/_clipboard_empty.zh-CN.html.erb
new file mode 100644
index 0000000..a6abd5b
--- /dev/null
+++ b/app/views/boxroom/clipboard/_clipboard_empty.zh-CN.html.erb
@@ -0,0 +1,2 @@
+剪贴板是空的
+<%= link_to '回去', '#', :class => 'back_link' %>用剪贴板按钮将文件或文件夹加到剪贴板。
diff --git a/app/views/boxroom/clipboard/_show.html.erb b/app/views/boxroom/clipboard/_show.html.erb
new file mode 100644
index 0000000..51abec8
--- /dev/null
+++ b/app/views/boxroom/clipboard/_show.html.erb
@@ -0,0 +1,70 @@
+<% if clipboard.empty? -%>
+
+ <%= image_tag('boxroom/information.png', :alt => 'Notice', :class => 'clipboard_info_image') %>
+ <%= render 'boxroom/clipboard/clipboard_empty' %>
+
+<% else -%>
+
+
+
+ <%= t :name %>
+
+
+ <% reset_cycle -%>
+ <% clipboard.folders.each do |item| -%>
+
+ <%= image_tag('boxroom/folder.png') %>
+ <%= item.name %>
+
+ <% if current_user.can_create(@folder) -%>
+ <%= link_to image_tag('boxroom/copy.png', :alt => t(:copy)),
+ { :controller => :clipboard, :action => :copy, :id => item.id, :type => 'folder', :folder_id => @folder, :authenticity_token => form_authenticity_token },
+ :method => :post, :title => t(:copy_folder)
+ %>
+ <% end -%>
+ <% if current_user.can_create(@folder) && current_user.can_delete(item) -%>
+ <%= link_to image_tag('boxroom/move.png', :alt => t(:move)),
+ { :controller => :clipboard, :action => :move, :id => item.id, :type => 'folder', :folder_id => @folder, :authenticity_token => form_authenticity_token },
+ :method => :post, :title => t(:move_folder), :data => { :confirm => t(:are_you_sure) }
+ %>
+ <% end -%>
+ <%= link_to image_tag('boxroom/delete.png', :alt => t(:delete_item)),
+ { :controller => :clipboard, :action => :destroy, :id => item.id, :type => 'folder', :folder_id => @folder, :authenticity_token => form_authenticity_token },
+ :method => :delete, :title => t(:remove_from_clipboard)
+ %>
+
+
+ <% end -%>
+ <% clipboard.files.each do |item| -%>
+
+ <%= image_tag(file_icon(item.extension)) %>
+ <%= item.attachment_file_name %>
+
+ <% if current_user.can_create(@folder) -%>
+ <%= link_to image_tag('boxroom/copy.png', :alt => t(:copy)),
+ { :controller => :clipboard, :action => :copy, :id => item.id, :type => 'file', :folder_id => @folder, :authenticity_token => form_authenticity_token },
+ :method => :post, :title => t(:copy_file)
+ %>
+ <% end -%>
+ <% if current_user.can_create(@folder) && current_user.can_delete(item.folder) -%>
+ <%= link_to image_tag('boxroom/move.png', :alt => t(:move)),
+ { :controller => :clipboard, :action => :move, :id => item.id, :type => 'file', :folder_id => @folder, :authenticity_token => form_authenticity_token },
+ :method => :post, :title => t(:move_file), :data => { :confirm => t(:are_you_sure) }
+ %>
+ <% end -%>
+ <%= link_to image_tag('boxroom/delete.png', :alt => t(:delete_item)),
+ { :controller => :clipboard, :action => :destroy, :id => item.id, :type => 'file', :folder_id => @folder, :authenticity_token => form_authenticity_token },
+ :method => :delete, :title => t(:remove_from_clipboard)
+ %>
+
+
+ <% end -%>
+
+
+ <%= button_to t(:clear_clipboard),
+ { :controller => :clipboard, :action => :reset, :folder_id => @folder, :authenticity_token => form_authenticity_token }, :method => :put
+ %>
+ —
+ <%= link_to t(:back), '#', :class => 'back_link' %>
+
+<% end -%>
diff --git a/app/views/boxroom/files/edit.html.erb b/app/views/boxroom/files/edit.html.erb
new file mode 100644
index 0000000..895c6e9
--- /dev/null
+++ b/app/views/boxroom/files/edit.html.erb
@@ -0,0 +1,14 @@
+<% content_for :title, t(:rename_file) -%>
+
+<%= content_for :title %>
+<%= form_for @file, :url => { :action => 'update' } do |f| %>
+ <%= f.error_messages %>
+
+ <%= f.label :name %>:
+ <%= f.text_field :attachment_file_name %>
+
+
+ <%= f.submit t(:save) %> —
+ <%= link_to t(:back), @folder %>
+
+<% end %>
diff --git a/app/views/boxroom/files/new.html.erb b/app/views/boxroom/files/new.html.erb
new file mode 100644
index 0000000..befd99c
--- /dev/null
+++ b/app/views/boxroom/files/new.html.erb
@@ -0,0 +1,25 @@
+<% content_for :title, t(:upload_file) -%>
+
+<%= content_for :title %>
+<%= form_for [@target_folder, @file], :url => { :action => 'create' } do |f| %>
+
+ <%= hidden_field_tag :target_folder_id, @target_folder.id %>
+ <%= file_field_tag :attachment, :multiple => true, :name => 'user_file[attachment]' %>
+
+
+
+
+ <%= link_to t(:back), @target_folder %>
+
+<% end %>
+
+
diff --git a/app/views/boxroom/folders/_form.html.erb b/app/views/boxroom/folders/_form.html.erb
new file mode 100644
index 0000000..30b2d2d
--- /dev/null
+++ b/app/views/boxroom/folders/_form.html.erb
@@ -0,0 +1,9 @@
+<%= f.error_messages %>
+
+ <%= f.label :name %>:
+ <%= f.text_field :name, { :class => 'text_input' } %>
+
+
+ <%= f.submit t(:save) %> —
+ <%= link_to t(:back), @folder.parent %>
+
diff --git a/app/views/boxroom/folders/edit.html.erb b/app/views/boxroom/folders/edit.html.erb
new file mode 100644
index 0000000..91e9df7
--- /dev/null
+++ b/app/views/boxroom/folders/edit.html.erb
@@ -0,0 +1,6 @@
+<% content_for :title, t(:rename_folder) -%>
+
+<%= content_for :title %>
+<%= form_for @folder do |f| %>
+ <%= render :partial => 'form', :locals => { :f => f } %>
+<% end %>
diff --git a/app/views/boxroom/folders/new.html.erb b/app/views/boxroom/folders/new.html.erb
new file mode 100644
index 0000000..52693b8
--- /dev/null
+++ b/app/views/boxroom/folders/new.html.erb
@@ -0,0 +1,6 @@
+<% content_for :title, t(:new_folder) -%>
+
+<%= content_for :title %>
+<%= form_for [@target_folder, @folder] do |f| %>
+ <%= render :partial => 'form', :locals => { :f => f } %>
+<% end %>
diff --git a/app/views/boxroom/folders/show.html.erb b/app/views/boxroom/folders/show.html.erb
new file mode 100644
index 0000000..f803655
--- /dev/null
+++ b/app/views/boxroom/folders/show.html.erb
@@ -0,0 +1,92 @@
+<% content_for :title, @folder.name -%>
+
+<%= content_for :title %>
+<% unless @folder.is_root? -%>
+
+ <%= breadcrumbs(@folder) %>
+ <%= @folder.name %>
+
+<% end -%>
+
+<% if current_user.can_create(@folder) -%>
+
+
+<% end -%>
+<% if current_user.member_of_admins? -%>
+
+<% end -%>
+
+
+
+
+
+
+ <%= t :name %>
+ <%= t :size %>
+ <%= t :date_modified %>
+
+
+ <% unless @folder.is_root? -%>
+
+ <%= image_tag('boxroom/folder.png') %>
+ ↑ <%= link_to t(:up), @folder.parent, :title => @folder.parent.name %>
+ --
+ --
+
+
+ <% end -%>
+ <% @folder.children.each do |folder| -%>
+ <% if current_user.can_read(folder) -%>
+
+ <%= image_tag('boxroom/folder.png') %>
+ <%= link_to folder.name, folder %>
+ --
+ <%= l folder.updated_at, :format => :short %>
+
+ <% if current_user.can_update(folder) -%>
+ <%= link_to image_tag('boxroom/edit.png', :alt => t(:edit)), edit_folder_path(folder), :title => t(:edit) %>
+ <% end -%>
+ <% if current_user.can_delete(folder) -%>
+ <%= link_to image_tag('boxroom/delete.png', :alt => t(:delete_item)), folder, :method => :delete, :data => { :confirm => t(:are_you_sure) }, :title => t(:delete_item) %>
+ <% end -%>
+ <%= link_to image_tag('boxroom/clipboard_add.png', :alt => t(:add_to_clipboard)),
+ { :controller => :clipboard, :action => :create, :id => folder.id, :type => 'folder', :folder_id => @folder, :authenticity_token => form_authenticity_token },
+ :method => :post, :title => t(:add_to_clipboard)
+ %>
+
+
+ <% end -%>
+ <% end -%>
+ <% @folder.user_files.each do |file| -%>
+ <% if current_user.can_read(@folder) -%>
+
+ <%= image_tag(file_icon(file.extension)) %>
+ <%= link_to file.attachment_file_name, file_path(file), :title => "#{t(:download)} #{file.attachment_file_name}" %>
+ <%= number_to_human_size(file.attachment_file_size, :locale => I18n.locale) %>
+ <%= l file.updated_at, :format => :short %>
+
+ <% if current_user.can_update(file.folder) -%>
+ <%= link_to image_tag('boxroom/edit.png', :alt => t(:edit)), edit_file_path(file), :title => t(:edit) %>
+ <% end -%>
+ <% if current_user.can_delete(file.folder) -%>
+ <%= link_to image_tag('boxroom/delete.png', :alt => t(:delete_item)), file_path(file), :method => :delete, :data => { :confirm => t(:are_you_sure) }, :title => t(:delete_item) %>
+ <% end -%>
+ <%= link_to image_tag('boxroom/clipboard_add.png', :alt => t(:add_to_clipboard)),
+ { :controller => :clipboard, :action => :create, :id => file.id, :type => 'file', :folder_id => @folder, :authenticity_token => form_authenticity_token },
+ :method => :post, :title => t(:add_to_clipboard)
+ %>
+ <%= link_to image_tag('boxroom/share.png', :alt => t(:share)), new_file_share_link_path(file), :title => t(:share) %>
+
+
+ <% end -%>
+ <% end -%>
+
+
+<% if current_user.member_of_admins? -%>
+
+ <%= render 'boxroom/permissions/form' %>
+
+<% end -%>
+
+ <%= render 'boxroom/clipboard/show' %>
+
diff --git a/app/views/boxroom/groups/_form.html.erb b/app/views/boxroom/groups/_form.html.erb
new file mode 100644
index 0000000..877f415
--- /dev/null
+++ b/app/views/boxroom/groups/_form.html.erb
@@ -0,0 +1,11 @@
+<%= form_for @group do |f| %>
+ <%= f.error_messages %>
+
+ <%= f.label :name %>:
+ <%= f.text_field :name, { :class => 'text_input' } %>
+
+
+ <%= f.submit t(:save) %> —
+ <%= link_to t(:back), groups_url %>
+
+<% end %>
diff --git a/app/views/boxroom/groups/edit.html.erb b/app/views/boxroom/groups/edit.html.erb
new file mode 100644
index 0000000..9ac3dd2
--- /dev/null
+++ b/app/views/boxroom/groups/edit.html.erb
@@ -0,0 +1,4 @@
+<% content_for :title, t(:rename_group) -%>
+
+<%= content_for :title %>
+<%= render 'form' %>
diff --git a/app/views/boxroom/groups/index.html.erb b/app/views/boxroom/groups/index.html.erb
new file mode 100644
index 0000000..cf21f53
--- /dev/null
+++ b/app/views/boxroom/groups/index.html.erb
@@ -0,0 +1,29 @@
+<% content_for :title, t(:groups) -%>
+
+<%= content_for :title %>
+
+
+
+
+
+
+ <%= t :name %>
+
+
+<% @groups.each do |group| -%>
+
+ <% if group.admins_group? -%>
+ <%= image_tag('boxroom/group_grey.png') %>
+ <%= group.name %>
+
+ <% else -%>
+ <%= image_tag('boxroom/group.png') %>
+ <%= group.name %>
+
+ <%= link_to image_tag('boxroom/edit.png', :alt => t(:edit)), edit_group_path(group), :title => t(:edit) %>
+ <%= link_to image_tag('boxroom/delete.png', :alt => t(:delete_item)), group_path(group), :method => :delete, :data => { :confirm => t(:are_you_sure) }, :title => t(:delete_item) %>
+
+ <% end -%>
+
+<% end -%>
+
diff --git a/app/views/boxroom/groups/new.html.erb b/app/views/boxroom/groups/new.html.erb
new file mode 100644
index 0000000..72f78dd
--- /dev/null
+++ b/app/views/boxroom/groups/new.html.erb
@@ -0,0 +1,4 @@
+<% content_for :title, t(:new_group) -%>
+
+<%= content_for :title %>
+<%= render 'form' %>
diff --git a/app/views/boxroom/permissions/_form.html.erb b/app/views/boxroom/permissions/_form.html.erb
new file mode 100644
index 0000000..28990ca
--- /dev/null
+++ b/app/views/boxroom/permissions/_form.html.erb
@@ -0,0 +1,41 @@
+<%= form_tag update_multiple_permissions_path do %>
+ <%= hidden_field_tag '_method', 'put' %>
+
+
+ <%= submit_tag t(:save) %>
+ <%= check_box_tag :recursive, true %> <%= t :apply_changes_to_subfolders %>
+ —
+ <%= link_to t(:back), '#', :class => 'back_link' %>
+
+<% end -%>
diff --git a/app/views/boxroom/reset_password/_message.de.html.erb b/app/views/boxroom/reset_password/_message.de.html.erb
new file mode 100644
index 0000000..b6d4bcf
--- /dev/null
+++ b/app/views/boxroom/reset_password/_message.de.html.erb
@@ -0,0 +1,2 @@
+Geben Sie ihre E-Mail Adresse ein um eine Anleitung zur
+Rücksetzung Ihres Kennworts zu erhalten.
diff --git a/app/views/boxroom/reset_password/_message.en.html.erb b/app/views/boxroom/reset_password/_message.en.html.erb
new file mode 100644
index 0000000..6fb6b83
--- /dev/null
+++ b/app/views/boxroom/reset_password/_message.en.html.erb
@@ -0,0 +1,2 @@
+Submit your email address and we will send you an email
+with instructions on how to reset your password.
diff --git a/app/views/boxroom/reset_password/_message.es.html.erb b/app/views/boxroom/reset_password/_message.es.html.erb
new file mode 100644
index 0000000..6c2bf6f
--- /dev/null
+++ b/app/views/boxroom/reset_password/_message.es.html.erb
@@ -0,0 +1,2 @@
+Ingrese su cuenta de correo donde recibirá un correo
+con instrucciones de como reiniciar sus credenciales.
diff --git a/app/views/boxroom/reset_password/_message.fr.html.erb b/app/views/boxroom/reset_password/_message.fr.html.erb
new file mode 100644
index 0000000..532aa81
--- /dev/null
+++ b/app/views/boxroom/reset_password/_message.fr.html.erb
@@ -0,0 +1,2 @@
+Saisissez votre adresse email et vous recevrez un message
+contentant les instructions pour réinitialiser votre mot de passe.
\ No newline at end of file
diff --git a/app/views/boxroom/reset_password/_message.it.html.erb b/app/views/boxroom/reset_password/_message.it.html.erb
new file mode 100644
index 0000000..25214a3
--- /dev/null
+++ b/app/views/boxroom/reset_password/_message.it.html.erb
@@ -0,0 +1,2 @@
+Inserisci il tuo indirizzo email e riceverai una email
+con le istruzioni su come reimpostare la password.
diff --git a/app/views/boxroom/reset_password/_message.nl.html.erb b/app/views/boxroom/reset_password/_message.nl.html.erb
new file mode 100644
index 0000000..58bb75f
--- /dev/null
+++ b/app/views/boxroom/reset_password/_message.nl.html.erb
@@ -0,0 +1,2 @@
+Vul uw e-mailadres in en we zullen u een e-mail toesturen
+met instructies om uw wachtwoord te resetten.
diff --git a/app/views/boxroom/reset_password/_message.zh-CN.html.erb b/app/views/boxroom/reset_password/_message.zh-CN.html.erb
new file mode 100644
index 0000000..f62fbf6
--- /dev/null
+++ b/app/views/boxroom/reset_password/_message.zh-CN.html.erb
@@ -0,0 +1,2 @@
+请提供您的电子邮箱地址,
+我们将给您发邮件告诉您如何重设密码。
diff --git a/app/views/boxroom/reset_password/edit.html.erb b/app/views/boxroom/reset_password/edit.html.erb
new file mode 100644
index 0000000..2bf20ec
--- /dev/null
+++ b/app/views/boxroom/reset_password/edit.html.erb
@@ -0,0 +1,18 @@
+<% content_for :title, t(:reset_password) -%>
+
+<%= content_for :title %>
+<%= form_for @user, :url => { :action => 'update' } do |f| %>
+ <%= f.error_messages %>
+
+ <%= label_tag :password, t(:password) %>:
+ <%= f.password_field :password, { :class => 'text_input' } %>
+
+
+ <%= label_tag :confirm_password, t(:confirm_password) %>:
+ <%= f.password_field :password_confirmation, { :class => 'text_input' } %>
+
+
+ <%= f.submit t(:reset_password) %> —
+ <%= link_to t(:back), new_session_path %>
+
+<% end %>
diff --git a/app/views/boxroom/reset_password/new.html.erb b/app/views/boxroom/reset_password/new.html.erb
new file mode 100644
index 0000000..649a069
--- /dev/null
+++ b/app/views/boxroom/reset_password/new.html.erb
@@ -0,0 +1,16 @@
+<% content_for :title, t(:reset_password) -%>
+
+<%= content_for :title %>
+
+ <%= render 'message' %>
+
+<%= form_tag(reset_password_index_path) do %>
+
+ <%= label_tag :email, t(:email) %>:
+ <%= text_field_tag :email, nil, :class => 'text_input' %>
+
+
+ <%= submit_tag t(:send_email) %> —
+ <%= link_to t(:back), new_session_path %>
+
+<% end %>
diff --git a/app/views/boxroom/sessions/new.html.erb b/app/views/boxroom/sessions/new.html.erb
new file mode 100644
index 0000000..41e5c98
--- /dev/null
+++ b/app/views/boxroom/sessions/new.html.erb
@@ -0,0 +1,21 @@
+<% content_for :title, t(:sign_in) -%>
+
+<%= content_for :title %>
+<%= form_tag(sessions_path) do %>
+
+ <%= label_tag :username, t(:username) %>:
+ <%= text_field_tag :username, nil, :class => 'text_input' %>
+
+
+ <%= label_tag :password, t(:password) %>:
+ <%= password_field_tag :password, nil, :class => 'text_input' %>
+
+
+ <%= check_box_tag :remember_me, 'true' %>
+ <%= label_tag :remember_me, t(:remember_me) %>
+
+
+ <%= submit_tag t(:sign_in) %> —
+ <%= link_to t(:reset_password), new_reset_password_path %>
+
+<% end %>
diff --git a/app/views/boxroom/share_links/index.html.erb b/app/views/boxroom/share_links/index.html.erb
new file mode 100644
index 0000000..ea0187a
--- /dev/null
+++ b/app/views/boxroom/share_links/index.html.erb
@@ -0,0 +1,24 @@
+<% content_for :title, t(:shared_files) -%>
+
+<%= content_for :title %>
+
+
+
+
+ <%= t('activerecord.models.user_file') %>
+ <%= t(:shared_by) %>
+ <%= t('activerecord.attributes.share_link.emails') %>
+ <%= t('activerecord.attributes.share_link.expires_at') %>
+
+
+<% @share_links.each do |share_link| -%>
+
+ <%= image_tag file_icon(share_link.user_file.extension) %>
+ <%= link_to truncate(share_link.user_file.attachment_file_name, :length => 24), share_link.user_file.folder, :title => "#{share_link.user_file.attachment_file_name} (#{share_link.user_file.folder.name})" %>
+ <%= share_link.user.name %>
+ <%= truncate(share_link.emails, :length => 36) %>
+ <%= l share_link.link_expires_at, :format => :very_short %>
+ <%= link_to image_tag('boxroom/delete.png', :alt => t(:delete_item)), share_link_path(share_link), :method => :delete, :data => { :confirm => t(:are_you_sure) }, :title => t(:unshare) %>
+
+<% end -%>
+
diff --git a/app/views/boxroom/share_links/new.html.erb b/app/views/boxroom/share_links/new.html.erb
new file mode 100644
index 0000000..6644f4b
--- /dev/null
+++ b/app/views/boxroom/share_links/new.html.erb
@@ -0,0 +1,48 @@
+<% content_for :title, t(:share_file) -%>
+
+<%= content_for :title %>
+<%= form_for [@file, @share_link], :url => { :action => 'create' } do |f| %>
+ <%= f.error_messages :header_message => t(:share_link_could_not_be_sent) %>
+
+ <%= t :you_are_about_to_share_the_following_file %>:
+
+ <%= image_tag(file_icon(@file.extension)) %>
+ <%= link_to @file.attachment_file_name, @folder %>
+
+
+
+ <%= f.label :emails, t(:emails_to_share_with) %>:
+ (<%= t :comma_seperated %>)
+ <%= f.text_area :emails, :class => 'emails_to_share_with' %>
+
+ <%= t :number_of_charachters %>:
+
+ <%= @share_link.emails.nil? ? 0 : @share_link.emails.length %>
+
+ / 255
+
+
+
+ <%= f.label :message, t(:shared_message) %>: (<%= t :optional %>)
+ <%= f.text_area :message, :class => 'share_message' %>
+
+
+ <%= f.label t(:link_expires) %>:
+ <%= f.select :link_expires_at, options_for_select([
+ [t(:tomorrow), 1.day.from_now.end_of_day],
+ [t(:three_days_from_now), 3.day.from_now.end_of_day],
+ [t(:one_week_from_now), 1.week.from_now.end_of_day],
+ [t(:ten_days_from_now), 10.days.from_now.end_of_day],
+ [t(:two_weeks_from_now), 2.weeks.from_now.end_of_day],
+ [t(:three_weeks_from_now), 3.weeks.from_now.end_of_day],
+ [t(:one_month_from_now), 1.month.from_now.end_of_day],
+ [t(:two_months_from_now), 2.months.from_now.end_of_day],
+ [t(:three_months_from_now), 3.months.from_now.end_of_day],
+ [t(:half_year_from_now), 6.months.from_now.end_of_day]
+ ], 2.weeks.from_now.end_of_day) %>
+
+
+ <%= f.submit t(:share) %> —
+ <%= link_to t(:back), @folder %>
+
+<% end %>
diff --git a/app/views/boxroom/shared/_footer.html.erb b/app/views/boxroom/shared/_footer.html.erb
new file mode 100644
index 0000000..b0b0241
--- /dev/null
+++ b/app/views/boxroom/shared/_footer.html.erb
@@ -0,0 +1,5 @@
+
diff --git a/app/views/boxroom/shared/_header.html.erb b/app/views/boxroom/shared/_header.html.erb
new file mode 100644
index 0000000..16da834
--- /dev/null
+++ b/app/views/boxroom/shared/_header.html.erb
@@ -0,0 +1,11 @@
+
diff --git a/app/views/boxroom/shared/_menu.html.erb b/app/views/boxroom/shared/_menu.html.erb
new file mode 100644
index 0000000..dda195e
--- /dev/null
+++ b/app/views/boxroom/shared/_menu.html.erb
@@ -0,0 +1,13 @@
+
diff --git a/app/views/boxroom/signup/edit.html.erb b/app/views/boxroom/signup/edit.html.erb
new file mode 100644
index 0000000..03408c5
--- /dev/null
+++ b/app/views/boxroom/signup/edit.html.erb
@@ -0,0 +1,26 @@
+<% content_for :title, t(:sign_up) -%>
+
+<%= content_for :title %>
+<%= form_for @user, :url => { :action => 'update' } do |f| %>
+ <%= f.error_messages %>
+
+ <%= f.label :name %>:
+ <%= f.text_field :name, { :class => 'text_input' } %>
+
+
+ <%= f.label :email %>:
+ <%= f.text_field :email, { :class => 'text_input' } %>
+
+
+ <%= f.label :password %>:
+ <%= f.password_field :password, { :class => 'text_input' } %>
+
+
+ <%= label_tag t(:confirm_password) %>:
+ <%= f.password_field :password_confirmation, { :class => 'text_input' } %>
+
+
+ <%= f.submit t(:sign_up) %> —
+ <%= link_to t(:back), new_session_path %>
+
+<% end %>
diff --git a/app/views/boxroom/user_mailer/reset_password_email.de.text.erb b/app/views/boxroom/user_mailer/reset_password_email.de.text.erb
new file mode 100644
index 0000000..fea58f9
--- /dev/null
+++ b/app/views/boxroom/user_mailer/reset_password_email.de.text.erb
@@ -0,0 +1,18 @@
+Sehr geehrter Nutzer,
+
+Über die folgende URL können Sie ein neues Kennwort für Ihr Konto erstellen:
+
+ <%= edit_reset_password_url(@user.reset_password_token) %>
+
+Zur Erinnerung hier nochmals Ihre Anmeldedaten:
+
+ Benutzername: <%= @user.name %>
+ E-Mail: <%= @user.email %>
+
+Sollten Sie keine Anfrage zum Zurücksetzen Ihres Kennworts geschickt haben,
+ignorieren Sie diese Nachricht.
+Ihr altes Kennwort ist weiterhin gültig.
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/reset_password_email.en.text.erb b/app/views/boxroom/user_mailer/reset_password_email.en.text.erb
new file mode 100644
index 0000000..42c7f35
--- /dev/null
+++ b/app/views/boxroom/user_mailer/reset_password_email.en.text.erb
@@ -0,0 +1,17 @@
+Dear user,
+
+Please use the following URL to reset your Boxroom password:
+
+ <%= edit_reset_password_url(@user.reset_password_token) %>
+
+Just as a reminder, your account info:
+
+ Username: <%= @user.name %>
+ Email: <%= @user.email %>
+
+If you did not submit a request to reset your password, please ignore this email.
+Your current password is still valid.
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/reset_password_email.es.text.erb b/app/views/boxroom/user_mailer/reset_password_email.es.text.erb
new file mode 100644
index 0000000..150d808
--- /dev/null
+++ b/app/views/boxroom/user_mailer/reset_password_email.es.text.erb
@@ -0,0 +1,17 @@
+Estiamdo usuario,
+
+Use la siguiente URL para reiniciar sus credenciales de Boxroom:
+
+ <%= edit_reset_password_url(@user.reset_password_token) %>
+
+Información de su cuenta:
+
+ Usuario: <%= @user.name %>
+ Correo: <%= @user.email %>
+
+Si usted no solicitó reiniciar sus credenciales, por favor ignore este correo.
+Sus credenciales seguirán siendo validas.
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/reset_password_email.fr.text.erb b/app/views/boxroom/user_mailer/reset_password_email.fr.text.erb
new file mode 100644
index 0000000..296d357
--- /dev/null
+++ b/app/views/boxroom/user_mailer/reset_password_email.fr.text.erb
@@ -0,0 +1,17 @@
+Cher utilisateur,
+
+Veuillez cliquer le lien ci-dessous pour réinitialiser votre mot de passe Boxroom:
+
+ <%= edit_reset_password_url(@user.reset_password_token) %>
+
+A titre d'information, vos informations utilisateur:
+
+ Nom d'utilisateur: <%= @user.name %>
+ Email: <%= @user.email %>
+
+Si vous n'avez pas demandé à réinitialiser votre mot de passe, ignorez simplement ce message.
+Votre mot de passe actuel reste inchangé.
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/reset_password_email.it.text.erb b/app/views/boxroom/user_mailer/reset_password_email.it.text.erb
new file mode 100644
index 0000000..a22edd5
--- /dev/null
+++ b/app/views/boxroom/user_mailer/reset_password_email.it.text.erb
@@ -0,0 +1,17 @@
+Gentile utente,
+
+La preghiamo di utilizzare il seguente URL per reimpostare la sua password su BoxRoom:
+
+ <%= edit_reset_password_url(@user.reset_password_token) %>
+
+Le ricordiamo i dettagli dell'account:
+
+ Username: <%= @user.name %>
+ Email: <%= @user.email %>
+
+Se non ha inoltrato la richiesta per reimpostare la password, può ignorare questa email.
+La password corrente continuerà ad essere valida.
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/reset_password_email.nl.text.erb b/app/views/boxroom/user_mailer/reset_password_email.nl.text.erb
new file mode 100644
index 0000000..2003859
--- /dev/null
+++ b/app/views/boxroom/user_mailer/reset_password_email.nl.text.erb
@@ -0,0 +1,17 @@
+Geachte gebruiker,
+
+Gebruik onderstaande URL om uw wachtwoord te resetten:
+
+ <%= edit_reset_password_url(@user.reset_password_token) %>
+
+Ter herinnering, uw account informatie is:
+
+ Gebruikersnaam: <%= @user.name %>
+ E-mail: <%= @user.email %>
+
+Als u geen aanvraag heeft gedaan voor het resetten van uw wachtwoord kunt u deze e-mail negeren.
+Uw huidige wachtwoord is nog steeds geldig.
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/reset_password_email.zh-CN.text.erb b/app/views/boxroom/user_mailer/reset_password_email.zh-CN.text.erb
new file mode 100644
index 0000000..911d82d
--- /dev/null
+++ b/app/views/boxroom/user_mailer/reset_password_email.zh-CN.text.erb
@@ -0,0 +1,16 @@
+亲爱的用户:
+
+请用下面的网址重设您的Boxroom密码:
+
+ <%= edit_reset_password_url(@user.reset_password_token) %>
+
+提醒一下,您的户头情况:
+
+ 用户名: <%= @user.name %>
+ 邮箱: <%= @user.email %>
+
+如果您不曾提交要求重设密码,请忽略这封信。您当前的密码仍然有效。
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/share_link_email.de.text.erb b/app/views/boxroom/user_mailer/share_link_email.de.text.erb
new file mode 100644
index 0000000..2eca673
--- /dev/null
+++ b/app/views/boxroom/user_mailer/share_link_email.de.text.erb
@@ -0,0 +1,20 @@
+Sehr geehrter Empfänger,
+
+<%= @share_link.user.email %> hat Ihnen eine Datei freigegeben:
+
+ <%= @share_link.user_file.attachment_file_name %> (<%= number_to_human_size(@share_link.user_file.attachment_file_size, :locale => I18n.locale) %>)
+
+Benutzen Sie die folgende Adresse, um die Datei herunterzuladen:
+
+ <%= share_link_url(:id => @share_link.link_token) %>
+
+Diese URL wird am <%= l @share_link.link_expires_at, :format => :long %> ablaufen.
+
+<% unless @share_link.message.blank? -%>
+=== <%= t(:shared_message) %>: ===
+<%= strip_tags(@share_link.message) %>
+<% end -%>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/share_link_email.en.text.erb b/app/views/boxroom/user_mailer/share_link_email.en.text.erb
new file mode 100644
index 0000000..1223d9b
--- /dev/null
+++ b/app/views/boxroom/user_mailer/share_link_email.en.text.erb
@@ -0,0 +1,20 @@
+Dear recipient,
+
+<%= @share_link.user.email %> has shared a file with you:
+
+ <%= @share_link.user_file.attachment_file_name %> (<%= number_to_human_size(@share_link.user_file.attachment_file_size, :locale => I18n.locale) %>)
+
+Use the following URL to download your file:
+
+ <%= share_link_url(:id => @share_link.link_token) %>
+
+The URL above will expire at <%= l @share_link.link_expires_at, :format => :time_only %> on <%= l @share_link.link_expires_at, :format => :date_only %>.
+
+<% unless @share_link.message.blank? -%>
+=== <%= t(:shared_message) %>: ===
+<%= strip_tags(@share_link.message) %>
+<% end -%>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/share_link_email.es.text.erb b/app/views/boxroom/user_mailer/share_link_email.es.text.erb
new file mode 100644
index 0000000..e13eb18
--- /dev/null
+++ b/app/views/boxroom/user_mailer/share_link_email.es.text.erb
@@ -0,0 +1,20 @@
+Estimado,
+
+<%= @share_link.user.email %> ha compartido un archivo con usted:
+
+ <%= @share_link.user_file.attachment_file_name %> (<%= number_to_human_size(@share_link.user_file.attachment_file_size, :locale => I18n.locale) %>)
+
+Use la siguiente URL para descargar su archivo:
+
+ <%= share_link_url(:id => @share_link.link_token) %>
+
+La URL expirará a las <%= l @share_link.link_expires_at, :format => :time_only %> del <%= l @share_link.link_expires_at, :format => :date_only %>.
+
+<% unless @share_link.message.blank? -%>
+=== <%= t(:shared_message) %>: ===
+<%= strip_tags(@share_link.message) %>
+<% end -%>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/share_link_email.fr.text.erb b/app/views/boxroom/user_mailer/share_link_email.fr.text.erb
new file mode 100644
index 0000000..9b88fa1
--- /dev/null
+++ b/app/views/boxroom/user_mailer/share_link_email.fr.text.erb
@@ -0,0 +1,20 @@
+Cher destinataire,
+
+<%= @share_link.user.email %> a partagé un fichier avec vous:
+
+ <%= @share_link.user_file.attachment_file_name %> (<%= number_to_human_size(@share_link.user_file.attachment_file_size, :locale => I18n.locale) %>)
+
+Utilisez le lien suivant pour télécharger le fichier:
+
+ <%= share_link_url(:id => @share_link.link_token) %>
+
+Le lien ci-dessus expirera à <%= l @share_link.link_expires_at, :format => :time_only %> le <%= l @share_link.link_expires_at, :format => :date_only %>.
+
+<% unless @share_link.message.blank? -%>
+=== <%= t(:shared_message) %>: ===
+<%= strip_tags(@share_link.message) %>
+<% end -%>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/share_link_email.it.text.erb b/app/views/boxroom/user_mailer/share_link_email.it.text.erb
new file mode 100644
index 0000000..0373042
--- /dev/null
+++ b/app/views/boxroom/user_mailer/share_link_email.it.text.erb
@@ -0,0 +1,20 @@
+Gentile utente,
+
+<%= @share_link.user.email %> ha condiviso un file con te:
+
+ <%= @share_link.user_file.attachment_file_name %> (<%= number_to_human_size(@share_link.user_file.attachment_file_size, :locale => I18n.locale) %>)
+
+Clicca sul seguente URL per scaricare il file:
+
+ <%= share_link_url(:id => @share_link.link_token) %>
+
+L'URL indicato sopra scadrà <%= l @share_link.link_expires_at, :format => :long %>.
+
+<% unless @share_link.message.blank? -%>
+=== <%= t(:shared_message) %>: ===
+<%= strip_tags(@share_link.message) %>
+<% end -%>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/share_link_email.nl.text.erb b/app/views/boxroom/user_mailer/share_link_email.nl.text.erb
new file mode 100644
index 0000000..942221f
--- /dev/null
+++ b/app/views/boxroom/user_mailer/share_link_email.nl.text.erb
@@ -0,0 +1,20 @@
+Geachte ontvanger,
+
+<%= @share_link.user.email %> heeft een bestand met u gedeeld:
+
+ <%= @share_link.user_file.attachment_file_name %> (<%= number_to_human_size(@share_link.user_file.attachment_file_size, :locale => I18n.locale) %>)
+
+Gebruik onderstaande URL om het bestand te downloaden:
+
+ <%= share_link_url(:id => @share_link.link_token) %>
+
+De vervaldatum van bovenstaande URL is <%= l @share_link.link_expires_at, :format => :long %>.
+
+<% unless @share_link.message.blank? -%>
+=== <%= t(:shared_message) %>: ===
+<%= strip_tags(@share_link.message) %>
+<% end -%>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/share_link_email.zh-CN.text.erb b/app/views/boxroom/user_mailer/share_link_email.zh-CN.text.erb
new file mode 100644
index 0000000..84e8728
--- /dev/null
+++ b/app/views/boxroom/user_mailer/share_link_email.zh-CN.text.erb
@@ -0,0 +1,20 @@
+亲爱的收件人:
+
+<%= @share_link.user.email %> 与您共享了一个文件:
+
+ <%= @share_link.user_file.attachment_file_name %> (<%= number_to_human_size(@share_link.user_file.attachment_file_size, :locale => I18n.locale) %>)
+
+请用下面的链接下载文件:
+
+ <%= share_link_url(:id => @share_link.link_token) %>
+
+此链接会在<%= l @share_link.link_expires_at, :format => :time_only %>,<%= l @share_link.link_expires_at, :format => :date_only %>失效。
+
+<% unless @share_link.message.blank? -%>
+=== <%= t(:shared_message) %>: ===
+<%= strip_tags(@share_link.message) %>
+<% end -%>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/signup_email.de.text.erb b/app/views/boxroom/user_mailer/signup_email.de.text.erb
new file mode 100644
index 0000000..2e1bf07
--- /dev/null
+++ b/app/views/boxroom/user_mailer/signup_email.de.text.erb
@@ -0,0 +1,9 @@
+Dear recipient,
+
+This is an invitation to sign up for Boxroom. Please use the following URL to sign up:
+
+ <%= edit_signup_url(@user.signup_token) %>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/signup_email.en.text.erb b/app/views/boxroom/user_mailer/signup_email.en.text.erb
new file mode 100644
index 0000000..2e1bf07
--- /dev/null
+++ b/app/views/boxroom/user_mailer/signup_email.en.text.erb
@@ -0,0 +1,9 @@
+Dear recipient,
+
+This is an invitation to sign up for Boxroom. Please use the following URL to sign up:
+
+ <%= edit_signup_url(@user.signup_token) %>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/signup_email.es.text.erb b/app/views/boxroom/user_mailer/signup_email.es.text.erb
new file mode 100644
index 0000000..1f4c701
--- /dev/null
+++ b/app/views/boxroom/user_mailer/signup_email.es.text.erb
@@ -0,0 +1,9 @@
+Estimado,
+
+Esta es una invitacion para activar su cuenta Boxroom. Por favor use la siguiente URL:
+
+ <%= edit_signup_url(@user.signup_token) %>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/signup_email.fr.text.erb b/app/views/boxroom/user_mailer/signup_email.fr.text.erb
new file mode 100644
index 0000000..eb2bedf
--- /dev/null
+++ b/app/views/boxroom/user_mailer/signup_email.fr.text.erb
@@ -0,0 +1,9 @@
+Cher destinataire,
+
+Ceci est un mail d'invitation à Boxroom. Pour vous inscrire, veuillez utiliser le lien ci-dessous:
+
+ <%= edit_signup_url(@user.signup_token) %>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/signup_email.it.text.erb b/app/views/boxroom/user_mailer/signup_email.it.text.erb
new file mode 100644
index 0000000..2e1bf07
--- /dev/null
+++ b/app/views/boxroom/user_mailer/signup_email.it.text.erb
@@ -0,0 +1,9 @@
+Dear recipient,
+
+This is an invitation to sign up for Boxroom. Please use the following URL to sign up:
+
+ <%= edit_signup_url(@user.signup_token) %>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/signup_email.nl.text.erb b/app/views/boxroom/user_mailer/signup_email.nl.text.erb
new file mode 100644
index 0000000..3f5f9d9
--- /dev/null
+++ b/app/views/boxroom/user_mailer/signup_email.nl.text.erb
@@ -0,0 +1,10 @@
+Geachte ontvanger,
+
+Dit is een uitnodiging om u aan te melden voor Boxroom.
+Gebruik onderstaande URL om uw account aan te maken:
+
+ <%= edit_signup_url(@user.signup_token) %>
+
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/user_mailer/signup_email.zh-CN.text.erb b/app/views/boxroom/user_mailer/signup_email.zh-CN.text.erb
new file mode 100644
index 0000000..151e25a
--- /dev/null
+++ b/app/views/boxroom/user_mailer/signup_email.zh-CN.text.erb
@@ -0,0 +1,8 @@
+亲爱的收件人,
+
+此信邀请你注册Boxroom。请用下面的网址注册:
+
+ <%= edit_signup_url(@user.signup_token) %>
+--
+Boxroom
+http://boxroomapp.com/
diff --git a/app/views/boxroom/users/_form.html.erb b/app/views/boxroom/users/_form.html.erb
new file mode 100644
index 0000000..e71281f
--- /dev/null
+++ b/app/views/boxroom/users/_form.html.erb
@@ -0,0 +1,46 @@
+<%= form_for @user do |f| %>
+ <%= f.error_messages %>
+ <% unless @user.new_record? -%>
+
+ <%= f.label :name %>:
+ <%= f.text_field :name, { :class => 'text_input' } %>
+
+ <% end -%>
+
+ <%= f.label :email %>:
+ <%= f.text_field :email, { :class => 'text_input' } %>
+
+ <% unless @user.new_record? -%>
+
+ <%= f.label :password %>:
+ <%= f.password_field :password, { :class => 'text_input' } %>
+
+
+ <%= label_tag t(:confirm_password) %>:
+ <%= f.password_field :password_confirmation, { :class => 'text_input' } %>
+
+ <% end -%>
+ <% if signed_in? && current_user.member_of_admins? -%>
+
+ <%= t :member_of_these_groups %>:
+
+ <% if @user.is_admin -%>
+
+ <%= hidden_field_tag 'user[group_ids][]', Group.admins_group.id %>
+ Admins
+
+ <%= f.collection_check_boxes :group_ids, Group.all_except_admins, :id, :name %>
+ <% else -%>
+ <%= f.collection_check_boxes :group_ids, Group.all, :id, :name %>
+ <% end -%>
+
+
+ <% end -%>
+
+ <%= f.submit t(:save) %>
+ <% if @user != current_user -%>
+ —
+ <%= link_to t(:back), users_url %>
+ <% end -%>
+
+<% end %>
diff --git a/app/views/boxroom/users/edit.html.erb b/app/views/boxroom/users/edit.html.erb
new file mode 100644
index 0000000..c9f9b5b
--- /dev/null
+++ b/app/views/boxroom/users/edit.html.erb
@@ -0,0 +1,4 @@
+<% content_for :title, @title -%>
+
+<%= content_for :title %>
+<%= render 'form' %>
diff --git a/app/views/boxroom/users/index.html.erb b/app/views/boxroom/users/index.html.erb
new file mode 100644
index 0000000..0d15d89
--- /dev/null
+++ b/app/views/boxroom/users/index.html.erb
@@ -0,0 +1,60 @@
+<% content_for :title, t(:users) -%>
+
+<%= content_for :title %>
+
+
+
+
+<%= t :active_users %>
+
+
+
+ <%= t :name %>
+ <%= t :email %>
+
+
+<% @users.each do |user| -%>
+
+ <%= image_tag('boxroom/user.png') %>
+ <%= user.name %>
+ <%= user.email %>
+
+ <%= link_to image_tag('boxroom/edit.png', :alt => t(:edit)), edit_user_path(user), :title => t(:edit) %>
+ <% unless user.is_admin -%>
+ <%= link_to image_tag('boxroom/delete.png', :alt => t(:delete_item)), user_path(user), :method => :delete, :data => { :confirm => t(:are_you_sure) }, :title => t(:delete_item) %>
+ <% end -%>
+
+
+<% end -%>
+
+
+<% unless @new_users.blank? -%>
+<%= t :invited_users %>
+
+
+
+ <%= t :email %>
+ <%= t :expiration_date %>
+
+
+<% @new_users.each do |user| -%>
+
+ <%= image_tag('boxroom/user.png') %>
+ <%= user.email %>
+
+ <% if user.signup_token_expires_at > Time.now -%>
+ <%= l user.signup_token_expires_at, :format => :very_short %>
+ <% else -%>
+ <%= l user.signup_token_expires_at, :format => :very_short %>
+ <% end -%>
+
+
+ <%= link_to image_tag('boxroom/extend.png', :alt => t(:extend_expiration_date)), extend_user_path(user), :method => :put, :title => t(:extend_expiration_date) %>
+ <% unless user.is_admin -%>
+ <%= link_to image_tag('boxroom/delete.png', :alt => t(:delete_item)), user_path(user), :method => :delete, :data => { :confirm => t(:are_you_sure) }, :title => t(:delete_item) %>
+ <% end -%>
+
+
+<% end -%>
+
+<% end -%>
diff --git a/app/views/boxroom/users/new.html.erb b/app/views/boxroom/users/new.html.erb
new file mode 100644
index 0000000..41d442f
--- /dev/null
+++ b/app/views/boxroom/users/new.html.erb
@@ -0,0 +1,4 @@
+<% content_for :title, t(:new_user) -%>
+
+<%= content_for :title %>
+<%= render 'form' %>
diff --git a/app/views/layouts/boxroom/application.html.erb b/app/views/layouts/boxroom/application.html.erb
new file mode 100644
index 0000000..1374a37
--- /dev/null
+++ b/app/views/layouts/boxroom/application.html.erb
@@ -0,0 +1,39 @@
+
+
+
+<% if content_for? :title -%>
+ <%= Boxroom.configuration.site_name %> | <%= content_for :title %>
+<% else -%>
+ <%= Boxroom.configuration.site_name %>
+<% end -%>
+ <%= stylesheet_link_tag 'boxroom/application' %>
+ <%= javascript_include_tag 'boxroom/application' %>
+ <%= csrf_meta_tag %>
+
+
+
+
+ <% if flash[:notice] -%>
+
+
+ <%= flash[:notice] %>
+
+
+ <% end -%>
+ <% if flash[:alert] -%>
+
+
+ <%= flash[:alert] %>
+
+
+ <% end -%>
+ <%= render 'boxroom/shared/header' %>
+ <%= render 'boxroom/shared/menu' %>
+
+ <%= yield %>
+
+
+ <%= render 'boxroom/shared/footer' %>
+
+
+
diff --git a/bin/rails b/bin/rails
new file mode 100755
index 0000000..d6b505d
--- /dev/null
+++ b/bin/rails
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+# This command will automatically be run when you run "rails" with Rails gems
+# installed from the root of your application.
+
+ENGINE_ROOT = File.expand_path('../..', __FILE__)
+ENGINE_PATH = File.expand_path('../../lib/boxroom/engine', __FILE__)
+APP_PATH = File.expand_path('../../test/dummy/config/application', __FILE__)
+
+# Set up gems listed in the Gemfile.
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
+
+require 'rails/all'
+require 'rails/engine/commands'
diff --git a/boxroom.gemspec b/boxroom.gemspec
new file mode 100644
index 0000000..4ce4535
--- /dev/null
+++ b/boxroom.gemspec
@@ -0,0 +1,26 @@
+$:.push File.expand_path("../lib", __FILE__)
+
+# Maintain your gem's version:
+require "boxroom/version"
+
+# Describe your gem and declare its dependencies:
+Gem::Specification.new do |s|
+ s.name = "boxroom"
+ s.version = Boxroom::VERSION
+ s.authors = ["Serge Koba"]
+ s.email = ["s.koba@mobidev.biz"]
+ s.homepage = ""
+ s.summary = "File manager engine"
+ s.description = "File manager engine"
+ s.license = "MIT"
+
+ s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
+
+ s.add_dependency 'rails', "~> 5.0.2"
+ s.add_dependency 'dynamic_form'
+ s.add_dependency 'acts_as_tree'
+ s.add_dependency 'paperclip'
+ s.add_dependency 'jquery-fileupload-rails'
+
+ s.add_development_dependency "sqlite3"
+end
diff --git a/config/locales/de.yml b/config/locales/de.yml
new file mode 100644
index 0000000..fb45a14
--- /dev/null
+++ b/config/locales/de.yml
@@ -0,0 +1,412 @@
+# German translations for Boxroom
+# Original Rails translation by Clemens Kofler (clemens@railway.at)
+# and Alexander Dreher - http://github.com/alexdreher - Rails 3 update
+#
+# see: https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/de.yml
+
+de:
+ date:
+ formats:
+ default: "%d.%m.%Y"
+ short: "%e. %b"
+ long: "%e. %B %Y"
+ only_day: "%e"
+
+ day_names: [Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag]
+ abbr_day_names: [So, Mo, Di, Mi, Do, Fr, Sa]
+ month_names: [~, Januar, Februar, März, April, Mai, Juni, Juli, August, September, Oktober, November, Dezember]
+ abbr_month_names: [~, Jan, Feb, Mär, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez]
+ order:
+ - :day
+ - :month
+ - :year
+
+ time:
+ formats:
+ default: "%A, %d. %B %Y, %H:%M Uhr"
+ short: "%d. %B %Y, %H:%M Uhr"
+ very_short: "%d. %B %Y"
+ long: "%A, %d. %B %Y, %H:%M Uhr"
+ time: "%H:%M"
+
+ am: "vormittags"
+ pm: "nachmittags"
+
+ datetime:
+ distance_in_words:
+ half_a_minute: 'eine halbe Minute'
+ less_than_x_seconds:
+ one: 'weniger als eine Sekunde'
+ other: 'weniger als %{count} Sekunden'
+ x_seconds:
+ one: 'eine Sekunde'
+ other: '%{count} Sekunden'
+ less_than_x_minutes:
+ one: 'weniger als eine Minute'
+ other: 'weniger als %{count} Minuten'
+ x_minutes:
+ one: 'eine Minute'
+ other: '%{count} Minuten'
+ about_x_hours:
+ one: 'etwa eine Stunde'
+ other: 'etwa %{count} Stunden'
+ x_days:
+ one: 'ein Tag'
+ other: '%{count} Tage'
+ about_x_months:
+ one: 'etwa ein Monat'
+ other: 'etwa %{count} Monate'
+ x_months:
+ one: 'ein Monat'
+ other: '%{count} Monate'
+ almost_x_years:
+ one: 'fast ein Jahr'
+ other: 'fast %{count} Jahre'
+ about_x_years:
+ one: 'etwa ein Jahr'
+ other: 'etwa %{count} Jahre'
+ over_x_years:
+ one: 'mehr als ein Jahr'
+ other: 'mehr als %{count} Jahre'
+ prompts:
+ second: "Sekunden"
+ minute: "Minuten"
+ hour: "Stunden"
+ day: "Tag"
+ month: "Monat"
+ year: "Jahr"
+
+ number:
+ format:
+ precision: 2
+ separator: ','
+ delimiter: '.'
+ significant: false
+ strip_insignificant_zeros: false
+ currency:
+ format:
+ unit: '€'
+ format: '%n%u'
+ separator: ","
+ delimiter: ""
+ precision: 2
+ significant: false
+ strip_insignificant_zeros: false
+ percentage:
+ format:
+ delimiter: ""
+ precision:
+ format:
+ delimiter: ""
+ human:
+ format:
+ delimiter: ""
+ precision: 1
+ significant: true
+ strip_insignificant_zeros: true
+ storage_units:
+ # Storage units output formatting.
+ # %u is the storage unit, %n is the number (default: 2 MB)
+ format: "%n %u"
+ units:
+ byte:
+ one: "Byte"
+ other: "Bytes"
+ kb: "KB"
+ mb: "MB"
+ gb: "GB"
+ tb: "TB"
+ decimal_units:
+ format: "%n %u"
+ units:
+ unit: ""
+ thousand: Tausend
+ million: Millionen
+ billion:
+ one: Milliarde
+ others: Milliarden
+ trillion: Billionen
+ quadrillion:
+ one: Billiarde
+ others: Billiarden
+
+ support:
+ array:
+ words_connector: ", "
+ two_words_connector: " und "
+ last_word_connector: " und "
+ select:
+ prompt: "Bitte wählen:"
+
+ activemodel:
+ errors:
+ template:
+ header:
+ one: "Konnte %{model} nicht speichern: ein Fehler."
+ other: "Konnte %{model} nicht speichern: %{count} Fehler."
+ body: "Bitte überprüfen Sie die folgenden Felder:"
+ helpers:
+ select:
+ prompt: "Bitte wählen"
+
+ submit:
+ create: '%{model} erstellen'
+ update: '%{model} aktualisieren'
+ submit: '%{model} speichern'
+
+ errors:
+ format: "%{attribute} %{message}"
+
+ messages: &errors_messages
+ inclusion: "ist kein gültiger Wert"
+ exclusion: "ist nicht verfügbar"
+ invalid: "ist nicht gültig"
+ confirmation: "stimmt nicht"
+ accepted: "muss akzeptiert werden"
+ empty: "muss ausgefüllt werden"
+ blank: "muss ausgefüllt werden"
+ too_long: "ist zu lang (nicht mehr als %{count} Zeichen)"
+ too_short: "ist zu kurz (nicht weniger als %{count} Zeichen)"
+ wrong_length: "hat die falsche Länge (muss genau %{count} Zeichen haben)"
+ not_a_number: "ist keine Zahl"
+ greater_than: "muss größer als %{count} sein"
+ greater_than_or_equal_to: "muss größer oder gleich %{count} sein"
+ equal_to: "muss genau %{count} sein"
+ less_than: "muss kleiner als %{count} sein"
+ less_than_or_equal_to: "muss kleiner oder gleich %{count} sein"
+ odd: "muss ungerade sein"
+ even: "muss gerade sein"
+ not_an_integer: "muss ganzzahlig sein"
+
+ activerecord:
+ errors:
+ template:
+ header:
+ one: "Konnte %{model} nicht speichern: ein Fehler."
+ other: "Konnte %{model} nicht speichern: %{count} Fehler."
+ body: "Bitte überprüfen Sie die folgenden Felder:"
+
+ messages:
+ taken: "ist bereits vergeben"
+ record_invalid: "Gültigkeitsprüfung ist fehlgeschlagen: %{errors}"
+ <<: *errors_messages
+
+ full_messages:
+ format: "%{attribute} %{message}"
+
+ models:
+ clipboard: Zwischenablage
+ folder: Ordner
+ group: Gruppe
+ permission: Berechtigung
+ share_link: Share link
+ user: Benutzer
+ user_file: Datei
+
+ attributes:
+ folder:
+ name: Name
+ group:
+ name: Name
+ share_link:
+ emails: E-Mail Adressen
+ expires_at: Läuft ab am
+ user:
+ email: E-Mail
+ name: Name
+ password: Kennwort
+ user_file:
+ name: Name
+ attachment_file_name: Datei
+
+ # APPLICATION SPECIFIC
+
+ # general
+ back: Zurück
+ save: Speichern
+ name: Name
+ email: E-Mail
+ submit: Senden
+
+ your_changes_were_saved: Ihre Einstellungen wurden erfolgreich gespeichert.
+ already_deleted: "Jemand hat %{type} bereits gelöscht. Die Aktion wurde abgebrochen."
+
+ # admins/new
+ create_admin: Administrator-Zugang erstellen
+ no_administrator_yet: Es gibt noch keinen Administrator-Zugang zu Boxroom. Hier können Sie einen anlegen.
+ create_admin_account: Konto erstellen
+ admin_user_created_successfully: Das Konto wurde erfolgreich erstellt. Sie können sich nun einloggen.
+
+ # clipboard/_show
+ folder: Ordner
+ file: Datei
+ this_folder: dieser Ordner
+ this_file: diese Datei
+ copy: Kopieren
+ copy_folder: Ordner kopieren
+ copy_file: Datei kopieren
+ move: Verschieben
+ move_folder: Ordner verschieben
+ move_file: Datei verschieben
+ are_you_sure: Sind Sie sicher?
+ delete_item: Löschen
+ remove_from_clipboard: Aus der Zwischenablage entfernen
+ clear_clipboard: Zwischenablage leeren
+
+ # files/edit
+ rename_file: Datei umbenennen
+
+ # files/new
+ select_file: Datei wählen
+ upload_file: Dateien hochladen
+ upload: Hochladen
+ exists_already: bereits vorhanden
+
+ #folders/edit
+ rename_folder: Ordner umbenennen
+
+ #folders/new
+ new_folder: Neuen Ordner anlegen
+
+ #folders/show
+ create_a_new_folder: Neuer Ordner
+ upload_a_file: Dateien hochladen
+ permissions: Berechtigungen
+ clipboard: Zwischenablage
+ size: Größe
+ date_modified: Letzte Änderung
+ up: nach oben
+ edit: Bearbeiten
+ add_to_clipboard: Zur Zwischenablage hinzufügen
+ download: Herunterladen
+ share: Freigeben
+
+ #groups/edit
+ rename_group: Gruppe umbenennen
+
+ #groups/index
+ groups: Gruppen
+ create_a_new_group: Neue Gruppe anlegen
+
+ #groups/new
+ new_group: Neue Gruppe anlegen
+
+ #permissions/form
+ create_permission: Erstellen
+ read_permission: Lesen
+ update_permission: Aktualisieren
+ delete_permission: Löschen
+ apply_changes_to_subfolders: Änderungen auf Unterordner anwenden
+
+ create: Erstellen
+ read: Lesen
+ update: Aktualisieren
+ delete: Löschen
+
+ #reset_password/edit
+ password: Kennwort
+ reset_password: Kennwort zurücksetzen
+ send_email: Absenden
+
+ #signup/edit
+ sign_up: Sign up
+
+ #sessions
+ username: Benutzername
+ remember_me: Angemeldet bleiben
+ sign_in: Anmelden
+
+ #share_links/index
+ shared_by: Freigabe von
+ unshare: Freigabe aufheben
+
+ #share_links/new
+ this_share_link: Dieser Freigabe-Link
+ share_file: Datei freigeben
+ you_are_about_to_share_the_following_file: Sie möchten die folgende Datei freigeben
+ emails_to_share_with: 'E-Mail Adressen, für welche die Datei freigegeben werden soll'
+ comma_seperated: Komma-separiert
+ number_of_charachters: Anzahl d. Zeichen
+ link_expires: Ablaufdatum
+ tomorrow: Morgen
+ three_days_from_now: In drei Tagen
+ one_week_from_now: In einer Woche
+ ten_days_from_now: In zehn Tagen
+ two_weeks_from_now: In zwei Wochen
+ three_weeks_from_now: In drei Wochen
+ one_month_from_now: In einem Monat
+ two_months_from_now: In zwei Monaten
+ three_months_from_now: In drei Monaten
+ half_year_from_now: In einem halben Jahr
+ download_link_could_not_be_sent: Die Download-URL konnte nicht versendet werden.
+ are_invalid_due_to: "Die Adresse %{email} ist ungültig."
+ shared_successfully: Die Datei wurde erfolgreich freigegeben.
+ shared_message: Nachricht
+ optional: Nicht erforderlich
+
+ #shared/_header
+ hello: Hallo
+ settings: Einstellungen
+ sign_out: Abmelden
+
+ #shared/_menu
+ folders: Ordner
+ users: Benutzer
+ shared_files: Freigegebene Dateien
+
+ #users/_form
+ member_of_these_groups: Mitglied in diesen Gruppen
+ confirm_password: Kennwort bestätigen
+
+ #users/index
+ create_a_new_user: Neuen Benutzer anlegen
+ active_users: Active users
+ invited_users: Invited users
+ expiration_date: Expiration date
+ extend_expiration_date: Extend expiration date
+
+ #users/new
+ new_user: Neuen Benutzer anlegen
+
+ #admins/controller
+ admin_user_created_sucessfully: Der Administrator-Zugang wurde erfolgreich angelegt. Sie können sich nun anmelden.
+
+ #application_controller
+ no_permissions_for_this_type: "Sie haben keine Berechtigung um %{type} zu %{method}."
+
+ # clipboard_controller
+ added_to_clipboard: Erfolgreich zur Zwischenablage hinzugefügt.
+ could_not_copy: "Kopieren fehlgeschlagen. Ein %{type} dieses Namens existiert bereits."
+ could_not_move: "Verschieben fehlgeschlagen. Ein %{type} dieses Namens existiert bereits."
+ cannot_move_to_own_subfolder: Sie können einen Ordner nicht in einen seiner Unterordner verschieben.
+
+ # folders_controller
+ cannot_delete_root_folder: Das Haupt-Verzeichnis kann nicht gelöscht oder umbenannt werden.
+ no_delete_permissions_for_subfolder: "Sie haben keine Berechtigung, einen der Unterordner zu löschen."
+
+ # groups_controller
+ group_already_deleted: Jemand hat diese Gruppe bereits gelöscht. Die Aktion wurde abgebrochen.
+ admins_group_cannot_be_deleted: Die Administrator-Gruppe kann nicht gelöscht werden.
+
+ # reset_password_controller
+ instruction_email_sent: E-Mail erfolgreich verschickt. Bitte prüfen Sie Ihren Posteingang.
+ password_reset_successfully: Ihr Kennwort wurde erfolgreich zurückgesetzt. Sie können sich nun anmelden.
+ reset_url_expired: Die URL zum Rücksetzen des Kennworts ist leider abgelaufen. Bitte probieren Sie es nochmals.
+
+ # signup_controller
+ signed_up_successfully: Account created successfully. You can now sign in.
+ sign_url_expired: The URL for signing up expired. Please contact the administrator.
+
+ # sessions_controller
+ credentials_incorrect: Benutzername und/oder Kennwort falsch. Bitte probieren Sie es nochmals.
+
+ # users_controller
+ user_already_deleted: Jemand hat diesen Benutzer bereits gelöscht. Die Aktion wurde abgebrochen.
+ admin_user_cannot_be_deleted: Der Administrator-Zugang kann nicht gelöscht werden.
+ edit_user: Benutzer bearbeiten
+ account_settings: Kontoeinstellungen
+
+ # mailers/user_mailer
+ signup_email_subject: '[Boxroom] Sign up invitation'
+ reset_password_email_subject: '[Boxroom] Anleitung zum Ändern Ihres Kennworts'
+ share_link_email_subject: '[Boxroom] %{email} möchte Ihnen eine Datei freigeben'
diff --git a/config/locales/en.yml b/config/locales/en.yml
new file mode 100644
index 0000000..a2a81e6
--- /dev/null
+++ b/config/locales/en.yml
@@ -0,0 +1,405 @@
+# US English translations for Ruby on Rails
+#
+# Use this as the base for the locale file of your language.
+# see: https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/en-US.yml
+
+"en":
+ date:
+ formats:
+ default: "%Y-%m-%d"
+ short: "%b %d"
+ long: "%B %d, %Y"
+
+ day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
+ abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
+
+ month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
+ abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
+ order:
+ - :year
+ - :month
+ - :day
+
+ time:
+ formats:
+ default: "%a, %d %b %Y %H:%M:%S %z"
+ short: "%d %b %Y %H:%M"
+ very_short: "%d %b %Y"
+ long: "%B %d, %Y %H:%M"
+ date_only: "%B %d, %Y"
+ time_only: "%H:%M"
+ am: "am"
+ pm: "pm"
+
+ support:
+ array:
+ words_connector: ", "
+ two_words_connector: " and "
+ last_word_connector: ", and "
+
+ select:
+ prompt: "Please select"
+
+ number:
+ format:
+ separator: "."
+ delimiter: ","
+ precision: 3
+ significant: false
+ strip_insignificant_zeros: false
+
+ currency:
+ format:
+ format: "%u%n"
+ unit: "$"
+ separator: "."
+ delimiter: ","
+ precision: 2
+ significant: false
+ strip_insignificant_zeros: false
+
+ percentage:
+ format:
+ delimiter: ""
+
+ precision:
+ format:
+ delimiter: ""
+
+ human:
+ format:
+ delimiter: ""
+ precision: 3
+ significant: true
+ strip_insignificant_zeros: true
+ storage_units:
+ format: "%n %u"
+ units:
+ byte:
+ one: "Byte"
+ other: "Bytes"
+ kb: "KB"
+ mb: "MB"
+ gb: "GB"
+ tb: "TB"
+ decimal_units:
+ format: "%n %u"
+ units:
+ unit: ""
+ thousand: Thousand
+ million: Million
+ billion: Billion
+ trillion: Trillion
+ quadrillion: Quadrillion
+
+ datetime:
+ distance_in_words:
+ half_a_minute: "half a minute"
+ less_than_x_seconds:
+ one: "less than 1 second"
+ other: "less than %{count} seconds"
+ x_seconds:
+ one: "1 second"
+ other: "%{count} seconds"
+ less_than_x_minutes:
+ one: "less than a minute"
+ other: "less than %{count} minutes"
+ x_minutes:
+ one: "1 minute"
+ other: "%{count} minutes"
+ about_x_hours:
+ one: "about 1 hour"
+ other: "about %{count} hours"
+ x_days:
+ one: "1 day"
+ other: "%{count} days"
+ about_x_months:
+ one: "about 1 month"
+ other: "about %{count} months"
+ x_months:
+ one: "1 month"
+ other: "%{count} months"
+ about_x_years:
+ one: "about 1 year"
+ other: "about %{count} years"
+ over_x_years:
+ one: "over 1 year"
+ other: "over %{count} years"
+ almost_x_years:
+ one: "almost 1 year"
+ other: "almost %{count} years"
+ prompts:
+ year: "Year"
+ month: "Month"
+ day: "Day"
+ hour: "Hour"
+ minute: "Minute"
+ second: "Seconds"
+
+ helpers:
+ select:
+ prompt: "Please select"
+
+ submit:
+ create: 'Create %{model}'
+ update: 'Update %{model}'
+ submit: 'Save %{model}'
+
+ errors:
+ format: "%{attribute} %{message}"
+
+ messages: &errors_messages
+ inclusion: "is not included in the list"
+ exclusion: "is reserved"
+ invalid: "is invalid"
+ confirmation: "doesn't match"
+ accepted: "must be accepted"
+ empty: "can't be empty"
+ blank: "can't be blank"
+ exists_already: 'exists already'
+ too_long: "is too long (maximum is %{count} characters)"
+ too_short: "is too short (minimum is %{count} characters)"
+ wrong_length: "is the wrong length (should be %{count} characters)"
+ not_a_number: "is not a number"
+ not_an_integer: "must be an integer"
+ greater_than: "must be greater than %{count}"
+ greater_than_or_equal_to: "must be greater than or equal to %{count}"
+ equal_to: "must be equal to %{count}"
+ less_than: "must be less than %{count}"
+ less_than_or_equal_to: "must be less than or equal to %{count}"
+ odd: "must be odd"
+ even: "must be even"
+ invalid_characters: 'cannot contain any of these characters: < > : " / \ | ? *'
+
+ activerecord:
+ errors:
+ template:
+ header:
+ one: "1 error prohibited this %{model} from being saved"
+ other: "%{count} errors prohibited this %{model} from being saved"
+ body: "There were problems with the following fields:"
+
+ messages:
+ taken: "has already been taken"
+ record_invalid: "Validation failed: %{errors}"
+ <<: *errors_messages
+
+ full_messages:
+ format: "%{attribute} %{message}"
+
+ models:
+ clipboard: Clipboard
+ folder: Folder
+ group: Group
+ permission: Permission
+ share_link: Share link
+ user: User
+ user_file: File
+
+ attributes:
+ folder:
+ name: Name
+ group:
+ name: Name
+ share_link:
+ emails: Email addresses
+ expires_at: Expires
+ user:
+ email: Email
+ name: Name
+ password: Password
+ user_file:
+ name: Name
+ attachment_file_name: File
+
+ # APPLICATION SPECIFIC
+
+ # general
+ back: Back
+ save: Save
+ name: Name
+ email: Email
+ submit: Submit
+
+ your_changes_were_saved: Your changes were saved successfully.
+ already_deleted: "Sorry, but this %{type} was deleted already."
+
+ # admins/new
+ create_admin: Create admin
+ no_administrator_yet: Boxroom does not have an administrator yet. Create one here.
+ create_admin_account: Create admin account
+ admin_user_created_successfully: The admin user was created successfully. You can now sign in.
+
+ # clipboard/_show
+ folder: folder
+ file: file
+ this_folder: this folder
+ this_file: this file
+ copy: Copy
+ copy_folder: Copy folder
+ copy_file: Copy file
+ move: Move
+ move_folder: Move folder
+ move_file: Move file
+ are_you_sure: Are you sure?
+ delete_item: Delete
+ remove_from_clipboard: Remove from clipboard
+ clear_clipboard: Clear clipboard
+
+ # files/edit
+ rename_file: Rename file
+
+ # files/new
+ select_file: Select file
+ upload_file: Upload files
+ upload: Upload
+ exists_already: exists already
+
+ #folders/edit
+ rename_folder: Rename folder
+
+ #folders/new
+ new_folder: New folder
+
+ #folders/show
+ create_a_new_folder: Create a new folder
+ upload_a_file: Upload files
+ permissions: Permissions
+ clipboard: Clipboard
+ size: Size
+ date_modified: Date modified
+ up: up
+ edit: Edit
+ add_to_clipboard: Add to clipboard
+ download: Download
+ share: Share
+
+ #groups/edit
+ rename_group: Rename group
+
+ #groups/index
+ groups: Groups
+ create_a_new_group: Create a new group
+
+ #groups/new
+ new_group: New group
+
+ #permissions/form
+ create_permission: Create
+ read_permission: Read
+ update_permission: Update
+ delete_permission: Delete
+ apply_changes_to_subfolders: Apply changes to subfolders
+
+ create: create
+ read: read
+ update: update
+ delete: delete
+
+ #reset_password/edit
+ password: Password
+ reset_password: Reset password
+ send_email: Send
+
+ #signup/edit
+ sign_up: Sign up
+
+ #sessions
+ username: Username
+ remember_me: Remember me
+ sign_in: Sign in
+
+ #share_links/index
+ shared_by: Shared by
+ unshare: Unshare
+
+ #share_links/new
+ this_share_link: this share link
+ share_file: Share file
+ you_are_about_to_share_the_following_file: You are about to share the following file
+ emails_to_share_with: Email addresses of the people you wish to share this file with
+ comma_seperated: Comma seperated
+ number_of_charachters: Number of characters
+ link_expires: Expires
+ tomorrow: Tomorrow
+ three_days_from_now: Three days from now
+ one_week_from_now: A week from now
+ ten_days_from_now: Ten days from now
+ two_weeks_from_now: Two weeks from now
+ three_weeks_from_now: Three weeks from now
+ one_month_from_now: A month from now
+ two_months_from_now: Two months from now
+ three_months_from_now: Three months from now
+ half_year_from_now: A half year from now
+ share_link_could_not_be_sent: The download link could not be sent
+ are_invalid_due_to: "is invalid due to %{email}"
+ shared_successfully: The file was shared successfully.
+ shared_message: Message
+ optional: Optional
+
+ #shared/_header
+ hello: Hello
+ settings: Settings
+ sign_out: Sign out
+
+ #shared/_menu
+ folders: Folders
+ users: Users
+ shared_files: Shared files
+
+ #users/_form
+ member_of_these_groups: Member of these groups
+ confirm_password: Confirm password
+
+ #users/index
+ create_a_new_user: Create a new user
+ active_users: Active users
+ invited_users: Invited users
+ expiration_date: Expiration date
+ extend_expiration_date: Extend expiration date
+
+ #users/new
+ new_user: New user
+
+ #admins/controller
+ admin_user_created_sucessfully: The admin user was created successfully. You can now sign in.
+
+ #application_controller
+ no_permissions_for_this_type: "You don't have %{method} permissions for %{type}."
+
+ # clipboard_controller
+ added_to_clipboard: Successfully added to clipboard.
+ could_not_copy: "Couldn't copy. A %{type} with the same name exists already."
+ could_not_move: "Couldn't move. A %{type} with the same name exists already."
+ cannot_move_to_own_subfolder: You cannot move a folder to its own sub-folder.
+
+ # folders_controller
+ cannot_delete_root_folder: The root folder cannot be deleted or renamed.
+ no_delete_permissions_for_subfolder: You don't have delete permissions for one of the subfolders.
+
+ # groups_controller
+ group_already_deleted: Someone else deleted this group. Your action was cancelled.
+ admins_group_cannot_be_deleted: The admins group cannot be deleted or renamed.
+
+ # reset_password_controller
+ instruction_email_sent: "An email with instructions was sent if '%{email}' exists in our system."
+ password_reset_successfully: Your password was reset successfully. You can now sign in.
+ reset_url_expired: The URL for resetting your password expired. Please try again.
+
+ # signup_controller
+ signed_up_successfully: Account created successfully. You can now sign in.
+ sign_url_expired: The URL for signing up expired. Please contact the administrator.
+
+ # sessions_controller
+ credentials_incorrect: Username and/or password were incorrect. Try again.
+
+ # users_controller
+ user_already_deleted: Someone else deleted the user. Your action was cancelled.
+ admin_user_cannot_be_deleted: The admin user cannot be deleted.
+ edit_user: Edit user
+ account_settings: Account settings
+
+ # mailers/user_mailer
+ signup_email_subject: '[Boxroom] Sign up invitation'
+ reset_password_email_subject: '[Boxroom] Password reset instructions'
+ share_link_email_subject: '[Boxroom] %{email} shared a file with you'
diff --git a/config/locales/es.yml b/config/locales/es.yml
new file mode 100644
index 0000000..46640ab
--- /dev/null
+++ b/config/locales/es.yml
@@ -0,0 +1,401 @@
+# ES Spanish translations for Boxroom
+#
+
+
+"es":
+ date:
+ formats:
+ default: "%d-%m-%Y"
+ short: "%b %d"
+ long: "%B %d, %Y"
+
+ day_names: [Domingo, Lunes, Martes, Miércoles, Jueves, Viernes, Sábado]
+ abbr_day_names: [Dom, Lun, Mar, Mie, Jue, Vie, Sab]
+
+ month_names: [~, Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio, Agosto, Septiembre, Octubre, Noviembre, Diciembre]
+ abbr_month_names: [~, Ene, Feb, Mar, Abr, May, Jun, Jul, Ago, Sep, Oct, Nov, Dic]
+ order:
+ - :day
+ - :month
+ - :year
+
+ time:
+ formats:
+ default: "%a, %d %b %Y %H:%M:%S %z"
+ short: "%d %b %Y %H:%M"
+ very_short: "%d %b %Y"
+ long: "%B %d, %Y %H:%M"
+ date_only: "%d de %B del %Y"
+ time_only: "%H:%M"
+ am: "am"
+ pm: "pm"
+
+ support:
+ array:
+ words_connector: ", "
+ two_words_connector: " y "
+ last_word_connector: ", y "
+
+ select:
+ prompt: "Seleccione"
+
+ number:
+ format:
+ separator: "."
+ delimiter: ","
+ precision: 3
+ significant: false
+ strip_insignificant_zeros: false
+
+ currency:
+ format:
+ format: "%u%n"
+ unit: "$"
+ separator: "."
+ delimiter: ","
+ precision: 2
+ significant: false
+ strip_insignificant_zeros: false
+
+ percentage:
+ format:
+ delimiter: ""
+
+ precision:
+ format:
+ delimiter: ""
+
+ human:
+ format:
+ delimiter: ""
+ precision: 3
+ significant: true
+ strip_insignificant_zeros: true
+ storage_units:
+ format: "%n %u"
+ units:
+ byte:
+ one: "Byte"
+ other: "Bytes"
+ kb: "KB"
+ mb: "MB"
+ gb: "GB"
+ tb: "TB"
+ decimal_units:
+ format: "%n %u"
+ units:
+ unit: ""
+ thousand: Ciento
+ million: Millon
+ billion: Billon
+ trillion: Trillon
+ quadrillion: Cuadrillon
+
+ datetime:
+ distance_in_words:
+ half_a_minute: "minuto y medio"
+ less_than_x_seconds:
+ one: "menos de 1 segundo"
+ other: "menos de %{count} segundos"
+ x_seconds:
+ one: "1 segundo"
+ other: "%{count} segundos"
+ less_than_x_minutes:
+ one: "menos de un minuto"
+ other: "menos de %{count} minutos"
+ x_minutes:
+ one: "1 minuto"
+ other: "%{count} minutos"
+ about_x_hours:
+ one: "alrededor de 1 hora"
+ other: "alrededor de %{count} horas"
+ x_days:
+ one: "1 día"
+ other: "%{count} días"
+ about_x_months:
+ one: "alrededor de 1 mes"
+ other: "alrededor de %{count} meses"
+ x_months:
+ one: "1 mes"
+ other: "%{count} meses"
+ about_x_years:
+ one: "alrededor de 1 año"
+ other: "alrededor de %{count} años"
+ over_x_years:
+ one: "mas de 1 año"
+ other: "mas de %{count} años"
+ almost_x_years:
+ one: "casi 1 año"
+ other: "casi %{count} años"
+ prompts:
+ year: "Año"
+ month: "Mes"
+ day: "Día"
+ hour: "Hora"
+ minute: "Minuto"
+ second: "Segundos"
+
+ helpers:
+ select:
+ prompt: "Seleccione"
+
+ submit:
+ create: 'Crear %{model}'
+ update: 'Actualizar %{model}'
+ submit: 'Guardar %{model}'
+
+ errors:
+ format: "%{attribute} %{message}"
+
+ messages: &errors_messages
+ inclusion: "no está incluido en la lista"
+ exclusion: "es reservado"
+ invalid: "es inválido"
+ confirmation: "no concuerda"
+ accepted: "debe ser aceptado"
+ empty: "no puede estar vacío"
+ blank: "no puede estar vacío"
+ exists_already: 'ya existe'
+ too_long: "demaciado largo (maximo %{count} caracteres)"
+ too_short: "demaciado corto (minimo is %{count} caracteres)"
+ wrong_length: "longitud incorrecta (debe ser %{count} caracteres)"
+ not_a_number: "no es un número"
+ not_an_integer: "debe ser un entero"
+ greater_than: "debe ser mayor que %{count}"
+ greater_than_or_equal_to: "debe ser mayor o igual que %{count}"
+ equal_to: "debe ser igual a %{count}"
+ less_than: "debe ser menor que %{count}"
+ less_than_or_equal_to: "debe ser menor o igual que %{count}"
+ odd: "debe ser impar"
+ even: "debe ser par"
+ invalid_characters: 'no puede tener los caracteres: < > : " / \ | ? *'
+
+ activerecord:
+ errors:
+ template:
+ header:
+ one: "1 error impidió guardar %{model}"
+ other: "%{count} errores impidió guardar %{model}"
+ body: "Los siguientes campos tienen un problema:"
+
+ messages:
+ taken: "ya está en uso"
+ record_invalid: "Validación: %{errors}"
+ <<: *errors_messages
+
+ full_messages:
+ format: "%{attribute} %{message}"
+
+ models:
+ clipboard: Portapaleles
+ folder: Carpeta
+ group: Grupo
+ permission: Permisos
+ share_link: Compartir
+ user: Usuario
+ user_file: Archivo
+
+ attributes:
+ folder:
+ name: Nombre
+ group:
+ name: Nombre
+ share_link:
+ emails: Correo electrónico
+ expires_at: Expira
+ user:
+ email: Correo
+ name: Usuario (sin espacios)
+ password: Clave
+ user_file:
+ name: Nombre
+ attachment_file_name: Archivo
+
+ # APPLICATION SPECIFIC
+
+ # general
+ back: Atrás
+ save: Guardar
+ name: Nombre
+ email: Correo
+ submit: Enviar
+
+ your_changes_were_saved: Los cambios fueron guardados correctamente.
+ already_deleted: "%{type} ya fue borrado."
+
+ # admins/new
+ create_admin: Crear admin
+ no_administrator_yet: El Sistema de Gestion de Archivos no tiene un administrador. Crear uno.
+ create_admin_account: Crear admin
+ admin_user_created_successfully: El administrador fue creado. Iniciar sesión.
+
+ # clipboard/_show
+ folder: carpeta
+ file: archivo
+ this_folder: esta carpeta
+ this_file: este archivo
+ copy: Copiar
+ copy_folder: Copiar carpeta
+ copy_file: Copiar archivo
+ move: Mover
+ move_folder: Mover carpeta
+ move_file: Mover archivo
+ are_you_sure: Está seguro?
+ delete_item: Borrar
+ remove_from_clipboard: Borrar desde el portapepeles
+ clear_clipboard: Limpiar portpapeles
+
+ # files/edit
+ rename_file: Renombrar archivo
+
+ # files/new
+ select_file: Seleccionar archivp
+ upload_file: Subir archivos
+ upload: Subir
+ exists_already: ya existe
+
+ #folders/edit
+ rename_folder: Renombrar Carpeta
+
+ #folders/new
+ new_folder: Nueva Carpeta
+
+ #folders/show
+ create_a_new_folder: Crear nueva carpeta
+ upload_a_file: Subir Archivos
+ permissions: Permisos
+ clipboard: Portapapeles
+ size: KB/MB
+ date_modified: Fecha modificación
+ up: subir
+ edit: Editar
+ add_to_clipboard: Agregar al portapapeles
+ download: Descargar
+ share: Compartir
+
+ #groups/edit
+ rename_group: Renombrar grupo
+
+ #groups/index
+ groups: Grupos
+ create_a_new_group: Crear grupo
+
+ #groups/new
+ new_group: Nuevo grupo
+
+ #permissions/form
+ create_permission: Crear
+ read_permission: Leer
+ update_permission: Actualizar
+ delete_permission: Borrar
+ apply_changes_to_subfolders: Aplicar cabios a las subcarpetas
+
+ create: crear
+ read: leer
+ update: actualizar
+ delete: borrar
+
+ #reset_password/edit
+ password: Clave
+ reset_password: Cambiar clave
+ send_email: Enviar
+
+ #signup/edit
+ sign_up: Iniciar sesión
+
+ #sessions
+ username: Usuario
+ remember_me: Recordarme
+ sign_in: Iniciar sesión
+
+ #share_links/index
+ unshare: Descompartir
+
+ #share_links/new
+ this_share_link: este enlace
+ share_file: Compartir archivo
+ you_are_about_to_share_the_following_file: Compartiendo el archivo
+ emails_to_share_with: Correos electrónicos a enviar el enlace
+ comma_seperated: Separados por comas
+ number_of_charachters: Número de caracteres
+ link_expires: Expira
+ tomorrow: Mañana
+ three_days_from_now: En 3 días
+ one_week_from_now: En 1 semana
+ ten_days_from_now: En 10 días
+ two_weeks_from_now: En 2 semanas
+ three_weeks_from_now: En 3 semanas
+ one_month_from_now: En 1 mes
+ two_months_from_now: En 2 meses
+ three_months_from_now: En 3 meses
+ half_year_from_now: En 6 meses
+ share_link_could_not_be_sent: El enclace no se pudo enviar
+ are_invalid_due_to: "%{email} es inválido"
+ shared_successfully: El archivo fue compartido.
+
+ #shared/_header
+ hello: Usuario
+ settings: Configuraciones
+ sign_out: Desconectarse
+
+ #shared/_menu
+ folders: Carpetas
+ users: Usuarios
+ shared_files: Compartir Archivos
+
+ #users/_form
+ member_of_these_groups: Miembro de estos grupos
+ confirm_password: Confirmar clave
+
+ #users/index
+ create_a_new_user: Crear usuario
+ active_users: Usuarios Activos
+ invited_users: Invitados
+ expiration_date: Fecha de Expiración
+ extend_expiration_date: Extender fecha de expiración
+
+ #users/new
+ new_user: Nuevo usuario
+
+ #admins/controller
+ admin_user_created_sucessfully: El administrador ha sido creado.
+
+ #application_controller
+ no_permissions_for_this_type: "No tiene permisos en %{method} para %{type}."
+
+ # clipboard_controller
+ added_to_clipboard: Agregado al portapapeles.
+ could_not_copy: "No se pudo copiar. Ya existe un %{type} con el mismo nombre."
+ could_not_move: "No se pudo mover. Ya existe un %{type} con el mismo nombre."
+ cannot_move_to_own_subfolder: No se puede mover una carpeta en la subcarpeta.
+
+ # folders_controller
+ cannot_delete_root_folder: La carpeta raíz no se puede borrar o renombrar.
+ no_delete_permissions_for_subfolder: No tiene permisos para esta subcarpeta.
+
+ # groups_controller
+ group_already_deleted: Este grupo fue borrado.
+ admins_group_cannot_be_deleted: El grupo admins no se puede borrar o renombrar.
+
+ # reset_password_controller
+ instruction_email_sent: "Un correo fue enviado con las instrucciones a '%{email}'."
+ password_reset_successfully: La clave fue cambiada.
+ reset_url_expired: El enlace para cambiar la clave ha expirado, contacte al administrador.
+
+ # signup_controller
+ signed_up_successfully: Cuenta creada.
+ sign_url_expired: El enlace para iniciar sesión expiró, contacte al administrador.
+
+ # sessions_controller
+ credentials_incorrect: Usuario y/o clave incorrecta.
+
+ # users_controller
+ user_already_deleted: Usuario borrado.
+ admin_user_cannot_be_deleted: El administrador no puede borrarse.
+ edit_user: Editar usuario
+ account_settings: Configuración de cuenta
+
+ # mailers/user_mailer
+ signup_email_subject: 'Invitación al Sistema de Gestión de Archivos'
+ reset_password_email_subject: 'Gestión de Archivos: cambio de claves'
+ share_link_email_subject: 'Gestión de Archivos: se le ha compartido un enlace'
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
new file mode 100644
index 0000000..9c56dc3
--- /dev/null
+++ b/config/locales/fr.yml
@@ -0,0 +1,401 @@
+# US English translations for Ruby on Rails
+#
+# Use this as the base for the locale file of your language.
+# see: https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/en-US.yml
+
+"fr":
+ date:
+ abbr_day_names:
+ - dim
+ - lun
+ - mar
+ - mer
+ - jeu
+ - ven
+ - sam
+ abbr_month_names:
+ -
+ - jan.
+ - fév.
+ - mar.
+ - avr.
+ - mai
+ - juin
+ - juil.
+ - août
+ - sept.
+ - oct.
+ - nov.
+ - déc.
+ day_names:
+ - dimanche
+ - lundi
+ - mardi
+ - mercredi
+ - jeudi
+ - vendredi
+ - samedi
+ formats:
+ default: ! '%d/%m/%Y'
+ long: ! '%e %B %Y'
+ short: ! '%e %b'
+ month_names:
+ -
+ - janvier
+ - février
+ - mars
+ - avril
+ - mai
+ - juin
+ - juillet
+ - août
+ - septembre
+ - octobre
+ - novembre
+ - décembre
+ order:
+ - :day
+ - :month
+ - :year
+ datetime:
+ distance_in_words:
+ about_x_hours:
+ one: environ %{count} heure
+ other: environ %{count} heures
+ about_x_months:
+ one: environ %{count} mois
+ other: environ %{count} mois
+ about_x_years:
+ one: environ %{count} an
+ other: environ %{count} ans
+ almost_x_years:
+ one: presque %{count} an
+ other: presque %{count} ans
+ half_a_minute: une demi-minute
+ less_than_x_minutes:
+ one: moins de %{count} minute
+ other: moins de %{count} minutes
+ zero: moins d'une minute
+ less_than_x_seconds:
+ one: moins de %{count} seconde
+ other: moins de %{count} secondes
+ zero: moins d'une seconde
+ over_x_years:
+ one: plus de %{count} an
+ other: plus de %{count} ans
+ x_days:
+ one: ! '%{count} jour'
+ other: ! '%{count} jours'
+ x_minutes:
+ one: ! '%{count} minute'
+ other: ! '%{count} minutes'
+ x_months:
+ one: ! '%{count} mois'
+ other: ! '%{count} mois'
+ x_seconds:
+ one: ! '%{count} seconde'
+ other: ! '%{count} secondes'
+ prompts:
+ day: Jour
+ hour: Heure
+ minute: Minute
+ month: Mois
+ second: Seconde
+ year: Année
+ activerecord:
+ errors:
+ format: Le %{attribute} %{message}
+ messages:
+ accepted: doit être accepté(e)
+ blank: doit être rempli(e)
+ confirmation: ne concorde pas
+ empty: doit être rempli(e)
+ equal_to: doit être égal à %{count}
+ even: doit être pair
+ exclusion: n'est pas disponible
+ greater_than: doit être supérieur à %{count}
+ greater_than_or_equal_to: doit être supérieur ou égal à %{count}
+ inclusion: n'est pas inclus(e) dans la liste
+ invalid: n'est pas valide
+ less_than: doit être inférieur à %{count}
+ less_than_or_equal_to: doit être inférieur ou égal à %{count}
+ not_a_number: n'est pas un nombre
+ not_an_integer: doit être un nombre entier
+ odd: doit être impair
+ record_invalid: ! 'La validation a échoué : %{errors}'
+ taken: n'est pas disponible
+ too_long:
+ one: est trop long (pas plus de %{count} caractère)
+ other: est trop long (pas plus de %{count} caractères)
+ too_short:
+ one: est trop court (au moins %{count} caractère)
+ other: est trop court (au moins %{count} caractères)
+ wrong_length:
+ one: ne fait pas la bonne longueur (doit comporter %{count} caractère)
+ other: ne fait pas la bonne longueur (doit comporter %{count} caractères)
+ template:
+ body: ! 'Veuillez vérifier les champs suivants : '
+ header:
+ one: ! 'Impossible d''enregistrer ce(tte) %{model} : %{count} erreur'
+ other: ! 'Impossible d''enregistrer ce(tte) %{model} : %{count} erreurs'
+ helpers:
+ select:
+ prompt: Veuillez sélectionner
+ submit:
+ create: Créer un(e) %{model}
+ submit: Enregistrer ce(tte) %{model}
+ update: Modifier ce(tte) %{model}
+ number:
+ currency:
+ format:
+ delimiter: ! ' '
+ format: ! '%n %u'
+ precision: 2
+ separator: ! ','
+ significant: false
+ strip_insignificant_zeros: false
+ unit: €
+ format:
+ delimiter: ! ' '
+ precision: 3
+ separator: ! ','
+ significant: false
+ strip_insignificant_zeros: false
+ human:
+ decimal_units:
+ format: ! '%n %u'
+ units:
+ billion: milliard
+ million: million
+ quadrillion: million de milliards
+ thousand: millier
+ trillion: billion
+ unit: ''
+ format:
+ delimiter: ''
+ precision: 2
+ significant: true
+ strip_insignificant_zeros: true
+ storage_units:
+ format: ! '%n %u'
+ units:
+ byte:
+ one: octet
+ other: octets
+ gb: Go
+ kb: ko
+ mb: Mo
+ tb: To
+ percentage:
+ format:
+ delimiter: ''
+ precision:
+ format:
+ delimiter: ''
+ support:
+ array:
+ last_word_connector: ! ' et '
+ two_words_connector: ! ' et '
+ words_connector: ! ', '
+ time:
+ am: am
+ formats:
+ default: ! '%d %B %Y %H:%M:%S'
+ long: ! '%A %d %B %Y %H:%M'
+ short: ! '%d %b %H:%M'
+ very_short: "%d %b %Y"
+ date_only: "%B %d, %Y"
+ time_only: "%H:%M"
+ pm: pm
+
+ # APPLICATION SPECIFIC
+
+ # general
+ back: Retour
+ save: Sauvegarder
+ name: Nom
+ email: Email
+ submit: Valider
+
+ your_changes_were_saved: Vos modifications ont été sauvegardées.
+ already_deleted: "Désolé mais ce %{type} a déjà été supprimé."
+
+ # admins/new
+ create_admin: Créer un admin
+ no_administrator_yet: Boxroom n'a pas encore d'admin. Créez-en un ici.
+ create_admin_account: Créer un compte admin
+ admin_user_created_successfully: "L'admin est maintenant créé, vous pouvez vous connecter"
+
+ # clipboard/_show
+ folder: dossier
+ file: fichier
+ this_folder: ce dossier
+ this_file: ce fichier
+ copy: Copier
+ copy_folder: Copier dossier
+ copy_file: Copier fichier
+ move: Déplacer
+ move_folder: Déplacer dossier
+ move_file: Déplacer fichier
+ are_you_sure: Etes-vous sûr?
+ delete_item: supprimer
+ remove_from_clipboard: Supprimer du presse-papier
+ clear_clipboard: Vider le presse-papier
+
+ # files/edit
+ rename_file: Renommer fichier
+
+ # files/new
+ select_file: Choisir un fichier
+ upload_file: Déposer des fichiers
+ upload: Déposer
+ exists_already: existe déjà
+
+ #folders/edit
+ rename_folder: Renommer dossier
+
+ #folders/new
+ new_folder: Nouveau dossier
+
+ #folders/show
+ create_a_new_folder: Créer un nouveau dossier
+ upload_a_file: Déposer un fichier
+ permissions: Permissions
+ clipboard: Presse-papier
+ size: Taille
+ date_modified: Date de modification
+ up: haut
+ edit: Modifier
+ add_to_clipboard: Ajouter au presse-papier
+ download: Télécharger
+ share: Partager
+
+ #groups/edit
+ rename_group: Renommer groupe
+
+ #groups/index
+ groups: Groupes
+ create_a_new_group: Créer un nouveau groupe
+
+ #groups/new
+ new_group: Nouveau groupe
+
+ #permissions/form
+ create_permission: Créer
+ read_permission: Lire
+ update_permission: Modifier
+ delete_permission: Supprimer
+ apply_changes_to_subfolders: Appliquer les changements aux sous-dossiers
+
+ create: créer
+ read: lire
+ update: Modifier
+ delete: Supprimer
+
+ #reset_password/edit
+ password: Mot de passe
+ reset_password: Réinitialiser le mot de passe
+ send_email: Envoyer
+
+ #signup/edit
+ sign_up: Inscription
+
+ #sessions
+ username: Nom d'utilisateur
+ remember_me: Se souvenir de moi
+ sign_in: Identification
+
+ #share_links/index
+ shared_by: Partage par
+ unshare: Annuler le partage
+
+ #share_links/new
+ this_share_link: ce lien de partage
+ share_file: Partager le fichier
+ you_are_about_to_share_the_following_file: Vous êtes sur le point de partager le fichier suivant
+ emails_to_share_with: Adresses email des personnes avec qui vous souhaitez partager
+ comma_seperated: Séparés par une virgule
+ number_of_charachters: Nombre de caractères
+ link_expires: Expiration
+ tomorrow: Demain
+ three_days_from_now: Dans 3 jours
+ one_week_from_now: Dans une semaine
+ ten_days_from_now: Dans 10 jours
+ two_weeks_from_now: Dans 2 semaines
+ three_weeks_from_now: Dans 3 semaines
+ one_month_from_now: Dans un mois
+ two_months_from_now: Dans 2 mois
+ three_months_from_now: Dans 3 mois
+ half_year_from_now: Dans 6 mois
+ share_link_could_not_be_sent: Le lien n'a pu être envoyé
+ are_invalid_due_to: "est invalide à cause de %{email}"
+ shared_successfully: Le fichier a bien été partagé.
+ shared_message: Message
+ optional: Optionnel
+
+ #shared/_header
+ hello: Bonjour
+ settings: Réglages
+ sign_out: Déconnexion
+
+ #shared/_menu
+ folders: Dossiers
+ users: Fichiers
+ shared_files: Fichiers partagés
+
+ #users/_form
+ member_of_these_groups: Membre de ces groupes
+ confirm_password: Confirmer le mot de passe
+
+ #users/index
+ create_a_new_user: Créer un nouvel utilisateur
+ active_users: Utilisateurs actifs
+ invited_users: Utilisateurs invités
+ expiration_date: Date d'expiration
+ extend_expiration_date: Prolonger la durée de validité
+
+ #users/new
+ new_user: Nouvel utilisateur
+
+ #admins/controller
+ admin_user_created_sucessfully: "L'admin est maintenant créé, vous pouvez vous connecter"
+
+ #application_controller
+ no_permissions_for_this_type: "Vous n'avez pas la permission de %{method} %{type}."
+
+ # clipboard_controller
+ added_to_clipboard: Ajouté au presse-papier.
+ could_not_copy: "Copie impossible. Un %{type} du même nom existe déjà."
+ could_not_move: "Déplacement impossible. A %{type} du même nom existe déjà."
+ cannot_move_to_own_subfolder: Vous ne pouvez pas déplacer un dossier dans un de ses sous-dossiers.
+
+ # folders_controller
+ cannot_delete_root_folder: Le dossier racine ne peut pas être supprimé.
+ no_delete_permissions_for_subfolder: Vous n'avez pas les permissions pour un ou plusieurs sous-dossiers.
+
+ # groups_controller
+ group_already_deleted: Quelqu'un d'autre a déjà supprimé ce groupe. Action annulée
+ admins_group_cannot_be_deleted: Le groupe admins ne peut être supprimé ni renommé.
+
+ # reset_password_controller
+ instruction_email_sent: "Se '%{email}' est présent dans le système, vous allez recevoir un message avec les instructions."
+ password_reset_successfully: Mot de passe réinitialisé, vous pouvez maintenant vous connecter.
+ reset_url_expired: L'URL de réinitialisation a expiré, veuillez réessayer à nouveau.
+
+ # signup_controller
+ signed_up_successfully: "Votre compte est maintenant créé, vous pouvez vous connecter"
+ sign_url_expired: "L'URL de confirmation a expiré, veuillez contacter l'administrateur."
+
+ # sessions_controller
+ credentials_incorrect: Nom d'utilisateur ou mot de passe incorrect. Veuillez réessayer.
+
+ # users_controller
+ user_already_deleted: Quelqu'un d'autre a déjà supprimé l'utilisateur.
+ admin_user_cannot_be_deleted: L'admin ne peut être supprimé
+ edit_user: Modifier utilisateur
+ account_settings: Profil
+
+ # mailers/user_mailer
+ signup_email_subject: '[Boxroom] Invitiation à créer un compte'
+ reset_password_email_subject: '[Boxroom] Instructions de réinitialisation mot de passe'
+ share_link_email_subject: "[Boxroom] %{email} a partagé un fichier avec vous"
diff --git a/config/locales/it.yml b/config/locales/it.yml
new file mode 100644
index 0000000..cd14739
--- /dev/null
+++ b/config/locales/it.yml
@@ -0,0 +1,412 @@
+# IT Italian translations for Ruby on Rails
+# Author Jessica Marcon
+# Use this as the base for the locale file of your language.
+# see: https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/en-US.yml
+
+
+"it":
+ date:
+ formats:
+ default: "%d/%m/%Y"
+ short: "%d %b"
+ long: "%d %B %Y"
+
+ day_names: [Domenica, Lunedì, Martedì, Mercoledì, Giovedì, Venerdì, Sabato]
+ abbr_day_names: [Dom, Lun, Mar, Mer, Gio, Ven, Sab]
+
+ month_names: [~, Gennaio, Febbraio, Marzo, Aprile, Maggio, Giugno, Luglio, Agosto, Settembre, Ottobre, Novembre, Dicembre]
+ abbr_month_names: [~, Gen, Feb, Mar, Apr, Mag, Giu, Lug, Ago, Set, Ott, Nov, Dic]
+ order:
+ - :day
+ - :month
+ - :year
+
+ time:
+ formats:
+ default: "%a %d %b %Y, %H:%M %:z"
+ short: "%d %b %Y %H:%M"
+ very_short: "%d %b %Y"
+ long: "%d %B %Y, %H:%M"
+ am: "am"
+ pm: "pm"
+
+ support:
+ array:
+ words_connector: ", "
+ two_words_connector: " e "
+ last_word_connector: ", e "
+
+ select:
+ prompt: "Seleziona"
+
+ number:
+ format:
+ precision: 2
+ separator: ","
+ delimiter: "."
+ significant: false
+ strip_insignificant_zeros: false
+
+ currency:
+ format:
+ unit: "€"
+ format: "%u%n"
+ separator: ","
+ delimiter: "."
+ precision: 2
+ significant: false
+ strip_insignificant_zeros: false
+
+ percentage:
+ format:
+ delimiter: ""
+
+ precision:
+ format:
+ delimiter: ""
+
+ human:
+ format:
+ delimiter: ""
+ precision: 3
+ significant: true
+ strip_insignificant_zeros: true
+ storage_units:
+ format: "%n %u"
+ units:
+ byte:
+ one: "Byte"
+ other: "Bytes"
+ kb: "KB"
+ mb: "MB"
+ gb: "GB"
+ tb: "TB"
+ decimal_units:
+ format: "%n %u"
+ units:
+ unit: ""
+ thousand: "Mille"
+ million:
+ one: "Milione"
+ others: "Milioni"
+ billion:
+ one: "Miliardo"
+ others: "Miliardi"
+ trillion:
+ one: "Trilione"
+ others: "Trilioni"
+ quadrillion:
+ one: "Quadrilione"
+ others: "Quadrilioni"
+
+ datetime:
+ distance_in_words:
+ half_a_minute: "mezzo minuto"
+ less_than_x_seconds:
+ one: "meno di 1 secondo"
+ other: "meno di %{count} secondi"
+ x_seconds:
+ one: "1 secondo"
+ other: "%{count} secondi"
+ less_than_x_minutes:
+ one: "meno di un minuto"
+ other: "meno di %{count} minuti"
+ x_minutes:
+ one: "1 minuto"
+ other: "%{count} minuti"
+ about_x_hours:
+ one: "circa un'ora"
+ other: "circa %{count} ore"
+ x_days:
+ one: "1 giorno"
+ other: "%{count} giorni"
+ about_x_months:
+ one: "circa 1 mese"
+ other: "circa %{count} mesi"
+ x_months:
+ one: "1 mese"
+ other: "%{count} mese"
+ about_x_years:
+ one: "circa 1 anno"
+ other: "circa %{count} anni"
+ over_x_years:
+ one: "oltre 1 anno"
+ other: "oltre %{count} anni"
+ almost_x_years:
+ one: "quasi 1 anno"
+ other: "quasi %{count} anni"
+ prompts:
+ year: "Anno"
+ month: "Mese"
+ day: "Giorno"
+ hour: "Ora"
+ minute: "Minuto"
+ second: "Secondi"
+
+ helpers:
+ select:
+ prompt: "Seleziona"
+
+ submit:
+ create: 'Crea %{model}'
+ update: 'Carica %{model}'
+ submit: 'Salva %{model}'
+
+ errors:
+ format: "%{attribute} %{message}"
+
+ messages: &errors_messages
+ inclusion: "non è incluso nella lista"
+ exclusion: "è riservato"
+ invalid: "non è valido"
+ confirmation: "non corrisponde"
+ accepted: "dev'essere accettato"
+ empty: "non può essere vuoto"
+ blank: "non può essere vuoto"
+ exists_already: 'esiste già'
+ too_long: "è troppo lungo (massimo %{count} caratteri)"
+ too_short: "è troppo corto (minimo %{count} caratteri)"
+ wrong_length: "la lunghezza è sbagliata (dovrebbe essere di %{count} caratteri)"
+ not_a_number: "non è un numero"
+ not_an_integer: "deve essere un numero intero"
+ greater_than: "deve essere maggiore di %{count}"
+ greater_than_or_equal_to: "deve essere maggiore o uguale a %{count}"
+ equal_to: "deve essere uguale a %{count}"
+ less_than: "deve essere inferiore a %{count}"
+ less_than_or_equal_to: "deve essere inferiore o uguale a %{count}"
+ odd: "deve essere dispari"
+ even: "deve essere ancora"
+ invalid_characters: 'non può contenere questo tipo di carattere: < > : " / \ | ? *'
+
+ activerecord:
+ errors:
+ template:
+ header:
+ one: "1 errore ha impedito a %{model} di essere salvato"
+ other: "%{count} errori hanno impedito a %{model} di essere salvato"
+ body: "Sono stati rilevati dei problemi con questi campi:"
+
+ messages:
+ taken: "è già in uso"
+ record_invalid: "Validation failed: %{errors}"
+ <<: *errors_messages
+
+ full_messages:
+ format: "%{attribute} %{message}"
+
+ models:
+ clipboard: Appunti
+ folder: Cartella
+ group: Gruppo
+ permission: Permessi
+ share_link: Condividi link
+ user: Utente
+ user_file: File
+
+ attributes:
+ folder:
+ name: Nome
+ group:
+ name: Nome
+ share_link:
+ emails: Indirizzo Email
+ expires_at: Scade
+ user:
+ email: Email
+ name: Nome
+ password: Password
+ user_file:
+ name: Nome
+ attachment_file_name: File
+
+ # APPLICATION SPECIFIC
+
+ # general
+ back: Indietro
+ save: Salva
+ name: Nome
+ email: Email
+ submit: Invia
+
+ your_changes_were_saved: Le tue modifiche sono state salvate con successo.
+ already_deleted: "Qualcuno ha cancellato %{type}. Azione non valida."
+
+ # admins/new
+ create_admin: Crea amministratore
+ no_administrator_yet: "Boxroom non ha ancora un amministratore: aggiungilo ora."
+ create_admin_account: Crea account amministratore
+ admin_user_created_successfully: "L'utente amministratore è stato aggiunto con successo! Ora puoi accedere."
+
+ # clipboard/_show
+ folder: cartella
+ file: file
+ this_folder: questa cartella
+ this_file: questo file
+ copy: Copia
+ copy_folder: Copia cartella
+ copy_file: Copia file
+ move: Sposta
+ move_folder: Sposta cartella
+ move_file: Sposta file
+ are_you_sure: Sei sicuro?
+ delete_item: Elimina
+ remove_from_clipboard: Elimina dagli appunti
+ clear_clipboard: Cancella appunti
+
+ # files/edit
+ rename_file: Rinomina file
+
+ # files/new
+ select_file: Seleziona file
+ upload_file: Carica file
+ upload: Carica
+ exists_already: esiste già
+
+ #folders/edit
+ rename_folder: Rinomina cartella
+
+ #folders/new
+ new_folder: Nuova cartella
+
+ #folders/show
+ create_a_new_folder: Crea una nuova cartella
+ upload_a_file: Carica file
+ permissions: Permessi
+ clipboard: Appunti
+ size: Dimensione
+ date_modified: Data modifica
+ up: up
+ edit: Modifica
+ add_to_clipboard: Copia negli appunti
+ download: Scarica
+ share: Condividi
+
+ #groups/edit
+ rename_group: Rinomina gruppo
+
+ #groups/index
+ groups: Gruppi
+ create_a_new_group: Aggiungi un nuovo gruppo
+
+ #groups/new
+ new_group: Nuovo gruppo
+
+ #permissions/form
+ create_permission: Crea
+ read_permission: Lettura
+ update_permission: Modifica
+ delete_permission: Elimina
+ apply_changes_to_subfolders: Applica modifiche alle sottocartelle
+
+ create: crea
+ read: lettura
+ update: modifica
+ delete: elimina
+
+ #reset_password/edit
+ password: Password
+ reset_password: Modifica password
+ send_email: Invia
+
+ #signup/edit
+ sign_up: Sign up
+
+ #sessions
+ username: Username
+ remember_me: Ricordami
+ sign_in: Accedi
+
+ #share_links/index
+ shared_by: Condiviso da
+ unshare: Annulla la condivisione
+
+ #share_links/new
+ this_share_link: this share link
+ share_file: Condividi file
+ you_are_about_to_share_the_following_file: Stai per condividere il seguente file
+ emails_to_share_with: Inserisci gli indirizzi email con i quali vuoi condividere il file
+ comma_seperated: Separati da una virgola
+ number_of_charachters: Numero di caratteri
+ link_expires: Scadenza
+ tomorrow: Domani
+ three_days_from_now: Tre giorni da adesso
+ one_week_from_now: Una settimana da adesso
+ ten_days_from_now: Dieci giorni da adesso
+ two_weeks_from_now: Due settimane da adesso
+ three_weeks_from_now: Tre settimane da adesso
+ one_month_from_now: Un mese da adesso
+ two_months_from_now: Due mesi da adesso
+ three_months_from_now: Tre mesi da adesso
+ half_year_from_now: Sei mesi da adesso
+ share_link_could_not_be_sent: Il link per il download non è stato inviato
+ are_invalid_due_to: " %{email} non è valido"
+ shared_successfully: Il file è stato condiviso con successo.
+ shared_message: Messaggio
+ optional: Opzionale
+
+ #shared/_header
+ hello: Salve
+ settings: Impostazioni
+ sign_out: Esci
+
+ #shared/_menu
+ folders: Cartelle
+ users: Utenti
+ shared_files: Condivisione file
+
+ #users/_form
+ member_of_these_groups: Membro di questi gruppi
+ confirm_password: Conferma password
+
+ #users/index
+ create_a_new_user: Aggiungi un nuovo utente
+ active_users: Active users
+ invited_users: Invited users
+ expiration_date: Expiration date
+ extend_expiration_date: Extend expiration date
+
+ #users/new
+ new_user: Nuovo utente
+
+ #admins/controller
+ admin_user_created_sucessfully: "L'utente amministratore è stato aggiunto con successo! Ora puoi accedere."
+
+ #application_controller
+ no_permissions_for_this_type: "Non hai %{method} i permessi per %{type}."
+
+ # clipboard_controller
+ added_to_clipboard: Aggiunto con successo negli appunti.
+ could_not_copy: "Non puoi copiare. Il %{type} con lo stesso nome esiste già."
+ could_not_move: "Non puoi spostare. Il %{type} con lo stesso nome esiste già."
+ cannot_move_to_own_subfolder: Non puoi spostare la cartella nella propria sottocartella.
+
+ # folders_controller
+ cannot_delete_root_folder: La cartella principale non può essere cancellata o rinominata.
+ no_delete_permissions_for_subfolder: "Non hai il permesso di cancellare una sottocartella."
+
+ # groups_controller
+ group_already_deleted: Qualcuno ha cancellato questo gruppo. Azione non valida.
+ admins_group_cannot_be_deleted: Il gruppo amministratore non può essere cancellato o rinominato.
+
+ # reset_password_controller
+ instruction_email_sent: "L'email con le istruzioni è stata inviata. Controlla la tua email."
+ password_reset_successfully: La password è stata modificata con successo. Ora puoi accedere.
+ reset_url_expired: Il link per cambiare la password è scaduto. Riprova.
+
+ # signup_controller
+ signed_up_successfully: Account created successfully. You can now sign in.
+ sign_url_expired: The URL for signing up expired. Please contact the administrator.
+
+ # sessions_controller
+ credentials_incorrect: "Username e/o password errati. Riprova."
+
+ # users_controller
+ user_already_deleted: "Qualcuno ha cancellato l'utente. Azione non valida."
+ admin_user_cannot_be_deleted: "L'utente amministratore non può essere cancellato."
+ edit_user: Inserisci utente
+ account_settings: Impostazioni account
+
+ # mailers/user_mailer
+ signup_email_subject: '[Boxroom] Sign up invitation'
+ share_link_email_subject: '[Boxroom] %{email} ha condiviso un file con te'
+ reset_password_email_subject: '[Boxroom] Istruzioni per modificare la password'
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
new file mode 100644
index 0000000..2a6093d
--- /dev/null
+++ b/config/locales/nl.yml
@@ -0,0 +1,406 @@
+# Dutch translations for Ruby on Rails, based on US English template
+# Original version by Ariejan de Vroom
+# - Sponsored by Kabisa ICT - http://kabisa.nl
+# Rails 3 update by Floris Huetink (github: florish)
+
+nl:
+ date:
+ formats:
+ default: "%d/%m/%Y"
+ short: "%e %b"
+ long: "%e %B %Y"
+ only_day: "%e"
+
+ day_names: [zondag, maandag, dinsdag, woensdag, donderdag, vrijdag, zaterdag]
+ abbr_day_names: [zon, maa, din, woe, don, vri, zat]
+
+ month_names: [~, januari, februari, maart, april, mei, juni, juli, augustus, september, oktober, november, december]
+ abbr_month_names: [~, jan, feb, mar, apr, mei, jun, jul, aug, sep, okt, nov, dec]
+ order:
+ - :day
+ - :month
+ - :year
+
+ time:
+ formats:
+ default: "%a %d %b %Y %H:%M:%S %Z"
+ short: "%d %b %Y %H:%M"
+ very_short: "%d %b %Y"
+ long: "%d %B %Y %H:%M"
+ time: "%H:%M"
+ only_second: "%S"
+ am: "'s ochtends"
+ pm: "'s middags"
+
+ support:
+ array:
+ words_connector: ", "
+ two_words_connector: " en "
+ last_word_connector: " en "
+
+ select:
+ prompt: "Selecteer"
+
+ number:
+ format:
+ separator: ","
+ delimiter: "."
+ precision: 2
+ significant: false
+ strip_insignificant_zeros: false
+
+ currency:
+ format:
+ format: "%u%n"
+ unit: "€"
+ separator: ","
+ delimiter: "."
+ precision: 2
+ significant: false
+ strip_insignificant_zeros: false
+
+ percentage:
+ format:
+ delimiter: ""
+
+ precision:
+ format:
+ delimiter: ""
+
+ human:
+ format:
+ delimiter: ""
+ precision: 3
+ significant: true
+ strip_insignificant_zeros: true
+ storage_units:
+ format: "%n %u"
+ units:
+ byte:
+ one: "Byte"
+ other: "Bytes"
+ kb: "KB"
+ mb: "MB"
+ gb: "GB"
+ tb: "TB"
+ decimal_units:
+ format: "%n %u"
+ units:
+ unit: ""
+ thousand: "duizend"
+ million: "miljoen"
+ billion: "miljard"
+ trillion: "biljoen"
+ quadrillion: "biljard"
+
+ datetime:
+ distance_in_words:
+ half_a_minute: "een halve minuut"
+ less_than_x_seconds:
+ one: "minder dan \xC3\xA9\xC3\xA9n seconde"
+ other: "minder dan %{count} seconden"
+ x_seconds:
+ one: "1 seconde"
+ other: "%{count} seconden"
+ less_than_x_minutes:
+ one: "minder dan \xC3\xA9\xC3\xA9n minuut"
+ other: "minder dan %{count} minuten"
+ x_minutes:
+ one: "1 minuut"
+ other: "%{count} minuten"
+ about_x_hours:
+ one: "ongeveer \xC3\xA9\xC3\xA9n uur"
+ other: "ongeveer %{count} uur"
+ x_days:
+ one: "1 dag"
+ other: "%{count} dagen"
+ about_x_months:
+ one: "ongeveer \xC3\xA9\xC3\xA9n maand"
+ other: "ongeveer %{count} maanden"
+ x_months:
+ one: "1 maand"
+ other: "%{count} maanden"
+ about_x_years:
+ one: "ongeveer \xC3\xA9\xC3\xA9n jaar"
+ other: "ongeveer %{count} jaar"
+ over_x_years:
+ one: "meer dan \xC3\xA9\xC3\xA9n jaar"
+ other: "meer dan %{count} jaar"
+ almost_x_years:
+ one: "bijna \xC3\xA9\xC3\xA9n jaar"
+ other: "bijna %{count} jaar"
+ prompts:
+ year: "jaar"
+ month: "maand"
+ day: "dag"
+ hour: "uur"
+ minute: "minuut"
+ second: "seconde"
+
+ helpers:
+ select:
+ prompt: "Selecteer"
+
+ submit:
+ create: '%{model} toevoegen'
+ update: '%{model} opslaan'
+ submit: '%{model} opslaan'
+
+ errors:
+ format: "%{attribute} %{message}"
+
+ messages: &errors_messages
+ inclusion: "is niet in de lijst opgenomen"
+ exclusion: "is niet beschikbaar"
+ invalid: "is ongeldig"
+ confirmation: "klopt niet"
+ accepted: "moet worden geaccepteerd"
+ empty: " mag niet leeg zijn"
+ blank: "mag niet leeg zijn"
+ exists_already: 'bestaat al'
+ too_long: "is te lang (maximaal %{count} tekens)"
+ too_short: "is te kort (minimaal %{count} tekens)"
+ wrong_length: "heeft onjuiste lengte (moet %{count} tekens lang zijn)"
+ not_a_number: "is geen getal"
+ not_an_integer: "moet een geheel getal zijn"
+ greater_than: "moet groter zijn dan %{count}"
+ greater_than_or_equal_to: "moet groter dan of gelijk zijn aan %{count}"
+ equal_to: "moet gelijk zijn aan %{count}"
+ less_than: "moet minder zijn dan %{count}"
+ less_than_or_equal_to: "moet minder dan of gelijk zijn aan %{count}"
+ odd: "moet oneven zijn"
+ even: "moet even zijn"
+ invalid_characters: 'mag geen van deze tekens bevatten: < > : " / \ | ? *'
+
+ activerecord:
+ errors:
+ template:
+ header:
+ one: "1 fout gevonden: %{model} niet opgeslagen"
+ other: "%{count} fouten gevonden: %{model} niet opgeslagen"
+ body: "Controleer de volgende velden:"
+
+ messages:
+ taken: "is al in gebruik"
+ record_invalid: "Validatie mislukt: %{errors}"
+ <<: *errors_messages
+
+ full_messages:
+ format: "%{attribute} %{message}"
+
+ models:
+ clipboard: Klembord
+ folder: Map
+ group: Groep
+ permission: Bevoegdheid
+ share_link: Gedeelde link
+ user: Gebruiker
+ user_file: Bestand
+
+ attributes:
+ folder:
+ name: Naam
+ group:
+ name: Naam
+ share_link:
+ emails: E-mailadressen
+ expires_at: Vervaldatum
+ user:
+ email: E-mail
+ name: Naam
+ password: Wachtwoord
+ user_file:
+ name: Naam
+ attachment_file_name: Bestand
+
+ # APPLICATION SPECIFIC
+
+ # general
+ back: Terug
+ save: Opslaan
+ name: Naam
+ email: E-mail
+ submit: Opslaan
+
+ your_changes_were_saved: Uw wijzigingen zijn opgeslagen.
+ already_deleted: "Iemand heeft %{type} reeds verwijderd."
+
+ # admins/new
+ create_admin: Admin aanmaken
+ no_administrator_yet: Boxroom heeft nog geen administrator. Deze kunt u nu aanmaken.
+ create_admin_account: Admin gebruiker aanmaken
+ admin_user_created_successfully: De admin gebruiker is aangemaakt. U kunt nu inloggen.
+
+ # clipboard/_show
+ folder: map
+ file: bestand
+ this_folder: deze map
+ this_file: dit bestand
+ copy: Kopieer
+ copy_folder: Map kopiëren
+ copy_file: Bestand kopiëren
+ move: Verplaatsen
+ move_folder: Map verplaatsen
+ move_file: Bestand verplaatsen
+ are_you_sure: Weet u het zeker?
+ delete_item: Verwijderen
+ remove_from_clipboard: Verwijder van klembord
+ clear_clipboard: Klembord legen
+
+ # files/edit
+ rename_file: Bestandsnaam wijzigen
+
+ # files/new
+ select_file: Selecteer bestand
+ upload_file: Bestanden uploaden
+ upload: Uploaden
+ exists_already: bestaat al
+
+ #folders/edit
+ rename_folder: Mapnaam wijzigen
+
+ #folders/new
+ new_folder: Nieuwe map
+
+ #folders/show
+ create_a_new_folder: Nieuwe map aanmaken
+ upload_a_file: Bestanden uploaden
+ permissions: Bevoegdheden
+ clipboard: Klembord
+ size: Grootte
+ date_modified: Datum gewijzigd
+ up: omhoog
+ edit: Wijzigen
+ add_to_clipboard: Op klembord plaatsen
+ download: Download
+ share: Delen
+
+ #groups/edit
+ rename_group: Groepnaam wijzigen
+
+ #groups/index
+ groups: Groepen
+ create_a_new_group: Nieuwe groep aanmaken
+
+ #groups/new
+ new_group: Nieuwe groep
+
+ #permissions/form
+ create_permission: Aanmaken
+ read_permission: Lezen
+ update_permission: Wijzigen
+ delete_permission: Verwijderen
+ apply_changes_to_subfolders: Toepassen op submappen
+
+ create: aanmaak
+ read: lees
+ update: wijzig
+ delete: verwijder
+
+ #reset_password/edit
+ password: Wachtwoord
+ reset_password: Wachtwoord resetten
+ send_email: Verzenden
+
+ #signup/edit
+ sign_up: Aanmelden
+
+ #sessions
+ username: Gebruikersnaam
+ remember_me: Onthoud mij
+ sign_in: Inloggen
+
+ #share_links/index
+ shared_by: Gedeeld door
+ unshare: Delen ongedaan maken
+
+ #share_links/new
+ this_share_link: this share link
+ share_file: Bestand delen
+ you_are_about_to_share_the_following_file: U staat op het punt het volgende bestand te delen
+ emails_to_share_with: E-mailadressen van de mensen waarmee u dit bestand wilt delen
+ comma_seperated: Gescheiden door komma's
+ number_of_charachters: Aantal tekens
+ link_expires: Vervaldatum
+ tomorrow: Morgen
+ three_days_from_now: Over drie dagen
+ one_week_from_now: Over een week
+ ten_days_from_now: Over tien dagen
+ two_weeks_from_now: Over twee weken
+ three_weeks_from_now: Over drie weken
+ one_month_from_now: Over een maand
+ two_months_from_now: Over twee maanden
+ three_months_from_now: Over drie maanden
+ half_year_from_now: Over een half jaar
+ share_link_could_not_be_sent: De download link kon niet verstuurd worden
+ are_invalid_due_to: "is ongeldig vanwege %{email}"
+ shared_successfully: De download link is verstuurd.
+ shared_message: Bericht
+ optional: Niet verplicht
+
+ #shared/_header
+ hello: Hallo
+ settings: Instellingen
+ sign_out: Uitloggen
+
+ #shared/_menu
+ folders: Mappen
+ users: Gebruikers
+ shared_files: Gedeelde bestanden
+
+ #users/_form
+ member_of_these_groups: Lid van deze groepen
+ confirm_password: Bevestig wachtwoord
+
+ #users/index
+ create_a_new_user: Nieuwe gebruiker aanmaken
+ active_users: Actieve gebruikers
+ invited_users: Uitgenodigde gebruikers
+ expiration_date: Vervaldatum
+ extend_expiration_date: Vervaldatum uitstellen
+
+ #users/new
+ new_user: Nieuwe gebruiker
+
+ #admins/controller
+ admin_user_created_sucessfully: De admin gebruiker is aangemaakt. U kunt nu inloggen.
+
+ #application_controller
+ no_permissions_for_this_type: "U heeft geen %{method} bevoegdheden voor %{type}."
+
+ # clipboard_controller
+ added_to_clipboard: Succesvol toegevoegd aan klembord.
+ could_not_copy: "Kopiëren mislukt. Een %{type} met dezelfde naam bestaat al."
+ could_not_move: "Verplaatsen mislukt. Een %{type} met dezelfde naam bestaat al."
+ cannot_move_to_own_subfolder: U kunt een map niet verplaatsen naar zijn eigen submap.
+
+ # folders_controller
+ cannot_delete_root_folder: Root folder kan niet verwijderd of gewijzigd worden.
+ no_delete_permissions_for_subfolder: U heeft geen bevoegdheden om één van de submappen te verwijderen.
+
+ # groups_controller
+ group_already_deleted: Iemand anders heeft deze groep verwijderd. De actie is afgebroken.
+ admins_group_cannot_be_deleted: De admin groep kan niet verwijderd of gewijzigd worden.
+
+ # reset_password_controller
+ instruction_email_sent: "Een e-mail met instructies is verzonden als '%{email}' aanwezig is in ons systeem."
+ password_reset_successfully: Uw wachtwoord is gereset. U kunt nu inloggen.
+ reset_url_expired: De URL om uw wachtwoord te resetten is ongeldig. Probeer het nogmaals.
+
+ # signup_controller
+ signed_up_successfully: Account aangemaakt. U kunt nu inloggen.
+ sign_url_expired: The URL om aan te melden is ongeldig. Neem contact op met de administrator.
+
+ # sessions_controller
+ credentials_incorrect: Gebruikersnaam en/of wachtwoord was incorrect. Probeer het nogmaals.
+
+ # users_controller
+ user_already_deleted: Iemand anders heeft deze gebruiker verwijderd. De actie is afgebroken.
+ admin_user_cannot_be_deleted: De admin gebruiker kan niet verwijderd worden.
+ edit_user: Gebruiker wijzigen
+ account_settings: Mijn instellingen
+
+ # mailers/user_mailer
+ signup_email_subject: '[Boxroom] Uitnodiging om aan te melden'
+ reset_password_email_subject: '[Boxroom] Instructies om uw wachtwoord te resetten'
+ share_link_email_subject: '[Boxroom] %{email} heeft een bestand met u gedeeld'
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
new file mode 100644
index 0000000..fabde23
--- /dev/null
+++ b/config/locales/zh-CN.yml
@@ -0,0 +1,404 @@
+# Simplified Chinese translations
+
+# The base of this locale file is https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/zh-CN.yml
+
+"zh-CN":
+ date:
+ formats:
+ default: "%Y-%m-%d"
+ short: "%b%d日"
+ long: "%Y年%b%d日"
+
+ day_names: [星期天, 星期一, 星期二, 星期三, 星期四, 星期五, 星期六]
+ abbr_day_names: [周日, 周一, 周二, 周三, 周四, 周五, 周六]
+
+ month_names: [~, 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月]
+ abbr_month_names: [~, 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月]
+ order:
+ - :year
+ - :month
+ - :day
+
+ time:
+ formats:
+ default: "%a, %Y年%B%d日 %H:%M:%S %z"
+ short: "%Y年%B%d日 %H:%M"
+ very_short: "%Y年%B%d日"
+ long: "%Y年%B%d日 %H:%M"
+ date_only: "%Y年%B%d日"
+ time_only: "%H:%M"
+ am: 上午
+ pm: 下午
+
+ support:
+ array:
+ words_connector: ","
+ two_words_connector: "和"
+ last_word_connector: ",和"
+
+ select:
+ prompt: "请选择"
+
+ number:
+ format:
+ separator: "."
+ delimiter: ","
+ precision: 3
+ significant: false
+ strip_insignificant_zeros: false
+
+ currency:
+ format:
+ format: "%u%n"
+ unit: "¥"
+ separator: "."
+ delimiter: ","
+ precision: 2
+ significant: false
+ strip_insignificant_zeros: false
+
+ percentage:
+ format:
+ delimiter: ""
+
+ precision:
+ format:
+ delimiter: ""
+
+ human:
+ format:
+ delimiter: ""
+ precision: 3
+ significant: true
+ strip_insignificant_zeros: true
+ storage_units:
+ format: "%n %u"
+ units:
+ byte:
+ one: "字节"
+ other: "字节"
+ kb: "KB"
+ mb: "MB"
+ gb: "GB"
+ tb: "TB"
+ decimal_units:
+ format: "%n %u"
+ units:
+ unit: ""
+ thousand: 千
+ million: 百万
+ billion: 十亿
+ trillion: 万亿
+ quadrillion: 千万亿
+
+ datetime:
+ distance_in_words:
+ half_a_minute: "半分钟"
+ less_than_x_seconds:
+ one: "不到1秒"
+ other: "不到%{count}秒"
+ x_seconds:
+ one: "1秒"
+ other: "%{count}秒"
+ less_than_x_minutes:
+ one: "不到1分钟"
+ other: "不到%{count}分钟"
+ x_minutes:
+ one: "1分钟"
+ other: "%{count}分钟"
+ about_x_hours:
+ one: "大约1小时"
+ other: "大约%{count}小时"
+ x_days:
+ one: "1天"
+ other: "%{count}天"
+ about_x_months:
+ one: "大约1月"
+ other: "大约%{count}月"
+ x_months:
+ one: "1月"
+ other: "%{count}月"
+ about_x_years:
+ one: "大约1年"
+ other: "大约%{count}年"
+ over_x_years:
+ one: "1年多"
+ other: "%{count}年多"
+ almost_x_years:
+ one: "大约1年"
+ other: "大约%{count}年"
+ prompts:
+ year: "年"
+ month: "月"
+ day: "日"
+ hour: "时"
+ minute: "分"
+ second: "秒"
+
+ helpers:
+ select:
+ prompt: "请选择"
+
+ submit:
+ create: '新建%{model}'
+ update: '更新%{model}'
+ submit: '提交%{model}'
+
+ errors:
+ format: "%{attribute} %{message}"
+
+ messages: &errors_messages
+ inclusion: "不包含于列表中"
+ exclusion: "是保留关键字"
+ invalid: "是无效的"
+ confirmation: "不一致"
+ accepted: "必须同意"
+ empty: "不能为空"
+ blank: "不能为空"
+ exists_already: '已经存在'
+ too_long: "太长(最多%{count}字节)"
+ too_short: "太短(至少%{count}字节)"
+ wrong_length: "长度不对(应该是%{count}字节)"
+ not_a_number: "不是数字"
+ not_an_integer: "必须是整数"
+ greater_than: "必须大于%{count}"
+ greater_than_or_equal_to: "必须大于或等于%{count}"
+ equal_to: "必须等于%{count}"
+ less_than: "必须小于%{count}"
+ less_than_or_equal_to: "必须小于或等于%{count}"
+ odd: "必须是单数"
+ even: "必须是双数"
+ invalid_characters: '不能包括这些字符: < > : " / \ | ? *'
+
+ activerecord:
+ errors:
+ template:
+ header:
+ one: "有1个错误导致%{model}无法被保存"
+ other: "有%{count}个错误发生导致%{model}无法被保存"
+ body: "如下字段出现错误"
+
+ messages:
+ taken: "已经被使用"
+ record_invalid: "验证失败: %{errors}"
+ <<: *errors_messages
+
+ full_messages:
+ format: "%{attribute} %{message}"
+
+ models:
+ clipboard: 剪贴板
+ folder: 文件夹
+ group: 群组
+ permission: 权限
+ share_link: 共享链接
+ user: 用户
+ user_file: 文件
+
+ attributes:
+ folder:
+ name: 名称
+ group:
+ name: 名称
+ share_link:
+ emails: 邮箱
+ expires_at: 失效期
+ user:
+ email: 邮箱
+ name: 用户名
+ password: 密码
+ user_file:
+ name: 文件名
+ attachment_file_name: 文件
+
+ # APPLICATION SPECIFIC
+
+ # general
+ back: 返回
+ save: 保存
+ name: 名字
+ email: 邮箱
+ submit: 提交
+
+ your_changes_were_saved: "你的修改已保存"
+ already_deleted: "对不起, 但是%{type}已被删掉了."
+
+ # admins/new
+ create_admin: 新增管理员
+ no_administrator_yet: Boxroom还没有管理员。请在此新增一个。
+ create_admin_account: 新增管理员户头
+ admin_user_created_successfully: 管理员户头已添加成功。您可以登录了。
+
+ # clipboard/_show
+ folder: 文件夹
+ file: 文件
+ this_folder: 此文件夹
+ this_file: 此文件
+ copy: 拷贝
+ copy_folder: 拷贝文件夹
+ copy_file: 拷贝文件
+ move: 移动
+ move_folder: 移动文件夹
+ move_file: 移动文件
+ are_you_sure: 您确定?
+ delete_item: 删除
+ remove_from_clipboard: 从剪贴板删除
+ clear_clipboard: 清空剪贴板
+
+ # files/edit
+ rename_file: 重新命名文件
+
+ # files/new
+ select_file: 选择文件
+ upload_file: 上传文件
+ upload: 上传
+ exists_already: 已存在
+
+ #folders/edit
+ rename_folder: 重新命名文件夹
+
+ #folders/new
+ new_folder: 新文件夹
+
+ #folders/show
+ create_a_new_folder: 新增新文件夹
+ upload_a_file: 上传文件
+ permissions: 权限
+ clipboard: 剪贴板
+ size: 大小
+ date_modified: 修改日期
+ up: 上一层
+ edit: 编辑
+ add_to_clipboard: 加到剪贴板
+ download: 下载
+ share: 共享
+
+ #groups/edit
+ rename_group: 群组重新命名
+
+ #groups/index
+ groups: 群组
+ create_a_new_group: 新增群组
+
+ #groups/new
+ new_group: 新群组
+
+ #permissions/form
+ create_permission: 新增
+ read_permission: 可读
+ update_permission: 可写
+ delete_permission: 删除
+ apply_changes_to_subfolders: 应用到所有子文件夹
+
+ create: 新增
+ read: 读
+ update: 写
+ delete: 删除
+
+ #reset_password/edit
+ password: 密码
+ reset_password: 重设密码
+ send_email: 发送
+
+ #signup/edit
+ sign_up: 注册
+
+ #sessions
+ username: 用户名
+ remember_me: 记住我
+ sign_in: 登录
+
+ #share_links/index
+ shared_by: 共享人
+ unshare: 撤销共享
+
+ #share_links/new
+ this_share_link: 这个共享链接
+ share_file: 共享文件
+ you_are_about_to_share_the_following_file: 您就要共享以下文件
+ emails_to_share_with: 您想与之共享文件的人的邮箱地址
+ comma_seperated: 由逗号分隔的
+ number_of_charachters: 字数
+ link_expires: 过期
+ tomorrow: 明天
+ three_days_from_now: 三天后
+ one_week_from_now: 一星期后
+ ten_days_from_now: 两天后
+ two_weeks_from_now: 两星期后
+ three_weeks_from_now: 三个星期后
+ one_month_from_now: 一个月后
+ two_months_from_now: 两个月后
+ three_months_from_now: 三个月后
+ half_year_from_now: 半年后
+ share_link_could_not_be_sent: 不能发送出下载链接
+ are_invalid_due_to: "因%{email}而无效"
+ shared_successfully: 成功共享文件
+ shared_message: 消息
+ optional: 可选
+
+ #shared/_header
+ hello: 您好
+ settings: 设置
+ sign_out: 退出
+
+ #shared/_menu
+ folders: 文件夹
+ users: 用户
+ shared_files: 共享的文件
+
+ #users/_form
+ member_of_these_groups: 属于群组
+ confirm_password: 确认密码
+
+ #users/index
+ create_a_new_user: 新增用户
+ active_users: 已激活的用户
+ invited_users: 已邀请的用户
+ expiration_date: 失效期
+ extend_expiration_date: 推迟失效期
+
+ #users/new
+ new_user: 新用户
+
+ #admins/controller
+ admin_user_created_sucessfully: 管理员用户已添加好。可以登录了。
+
+ #application_controller
+ no_permissions_for_this_type: "您没有对%{type}的%{method}权限."
+
+ # clipboard_controller
+ added_to_clipboard: 已成功添加到剪贴板。
+ could_not_copy: "不能拷贝。同样名字的%{type}已存在。"
+ could_not_move: "不能移动。同样名字的%{type}已存在。"
+ cannot_move_to_own_subfolder: 您不能把文件夹放到其子文件夹里。
+
+ # folders_controller
+ cannot_delete_root_folder: 根文件夹不能删除或重新命名。
+ no_delete_permissions_for_subfolder: 您没有删除其中某个子文件夹的权限。
+
+ # groups_controller
+ group_already_deleted: 有人已删掉此群组。您的操作已被撤销。
+ admins_group_cannot_be_deleted: 管理员群组不能被删掉。
+
+ # reset_password_controller
+ instruction_email_sent: "如果'%{email}'在本系统中,我们会发出邮件告诉您如何重设密码。"
+ password_reset_successfully: 您的密码已成功重设。您可以登录了。
+ reset_url_expired: 这个重设密码的网址已失效。请重试。
+
+ # signup_controller
+ signed_up_successfully: 户头注册成功。您可以登录了。
+ sign_url_expired: 这个注册户头的网址已失效。请联系管理员。
+
+ # sessions_controller
+ credentials_incorrect: 用户名或密码不正确。请重试。
+
+ # users_controller
+ user_already_deleted: 有人已删掉此用户。您的操作已被撤销。
+ admin_user_cannot_be_deleted: 此管理员不能被删除。
+ edit_user: 编辑用户
+ account_settings: 用户设置
+
+ # mailers/user_mailer
+ signup_email_subject: '[Boxroom] 注册邀请'
+ reset_password_email_subject: '[Boxroom] 重设密码的步骤'
+ share_link_email_subject: '[Boxroom] %{email}与您共享了文件'
diff --git a/config/routes.rb b/config/routes.rb
new file mode 100644
index 0000000..41723ea
--- /dev/null
+++ b/config/routes.rb
@@ -0,0 +1,43 @@
+Boxroom::Engine.routes.draw do
+ get '/file_exists', :to => 'files#exists'
+ get '/signin', :to => 'sessions#new', :as => 'signin'
+ delete '/signout', :to => 'sessions#destroy'
+
+ # Resources
+ resources :admins, :only => [:new, :create]
+ resources :sessions, :only => [:new, :create, :destroy]
+ resources :reset_password, :except => [:index, :show, :destroy]
+ resources :signup, :only => [:edit, :update]
+ resources :groups, :except => :show
+ resources :files, :except => [:index, :new, :create]
+ resources :share_links, :only => [:index, :show, :destroy]
+
+ resources :users, :except => :show do
+ put :extend, :on => :member
+ end
+
+ resources :clipboard, :only => [:create, :destroy] do
+ post :copy, :on => :member
+ post :move, :on => :member
+ put :reset, :on => :member
+ end
+
+ # Update a collection of permissions
+ resources :permissions, :only => :update_multiple do
+ put :update_multiple, :on => :collection
+ end
+
+ # Nested resources
+ resources :folders, :shallow => true, :except => [:new, :create] do
+ resources :folders, :only => [:new, :create]
+ resources :files, :only => [:new, :create]
+ end
+
+ resources :files, :shallow => :true, :only => :show do
+ resources :share_links, :only => [:new, :create]
+ end
+
+ # You can have the root of your site routed with "root"
+ # just remember to delete public/index.html.
+ root :to => "folders#index"
+end
diff --git a/db/migrate/20100930062939_create_users.rb b/db/migrate/20100930062939_create_users.rb
new file mode 100644
index 0000000..aec191a
--- /dev/null
+++ b/db/migrate/20100930062939_create_users.rb
@@ -0,0 +1,20 @@
+class CreateUsers < ActiveRecord::Migration
+ def self.up
+ create_table :boxroom_users do |t|
+ t.string :name
+ t.string :email
+ t.string :hashed_password
+ t.string :password_salt
+ t.string :is_admin
+ t.string :access_key
+ t.string :remember_token
+ t.string :reset_password_token
+ t.datetime :reset_password_token_expires_at
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :boxroom_users
+ end
+end
diff --git a/db/migrate/20100930091426_create_folders.rb b/db/migrate/20100930091426_create_folders.rb
new file mode 100644
index 0000000..19490b4
--- /dev/null
+++ b/db/migrate/20100930091426_create_folders.rb
@@ -0,0 +1,14 @@
+class CreateFolders < ActiveRecord::Migration
+ def self.up
+ create_table :boxroom_folders do |t|
+ t.string :name
+ t.references :user
+ t.references :parent
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :boxroom_folders
+ end
+end
diff --git a/db/migrate/20100930091451_create_groups.rb b/db/migrate/20100930091451_create_groups.rb
new file mode 100644
index 0000000..3200872
--- /dev/null
+++ b/db/migrate/20100930091451_create_groups.rb
@@ -0,0 +1,12 @@
+class CreateGroups < ActiveRecord::Migration
+ def self.up
+ create_table :boxroom_groups do |t|
+ t.string :name
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :boxroom_groups
+ end
+end
diff --git a/db/migrate/20101002122244_create_user_files.rb b/db/migrate/20101002122244_create_user_files.rb
new file mode 100644
index 0000000..9740141
--- /dev/null
+++ b/db/migrate/20101002122244_create_user_files.rb
@@ -0,0 +1,17 @@
+class CreateUserFiles < ActiveRecord::Migration
+ def self.up
+ create_table :boxroom_user_files do |t|
+ t.string :attachment_file_name
+ t.string :attachment_content_type
+ t.integer :attachment_file_size
+ t.datetime :attachment_updated_at
+ t.references :folder
+ t.references :user
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :boxroom_user_files
+ end
+end
diff --git a/db/migrate/20101005071402_create_permissions.rb b/db/migrate/20101005071402_create_permissions.rb
new file mode 100644
index 0000000..615d0e5
--- /dev/null
+++ b/db/migrate/20101005071402_create_permissions.rb
@@ -0,0 +1,16 @@
+class CreatePermissions < ActiveRecord::Migration
+ def self.up
+ create_table :boxroom_permissions do |t|
+ t.references :folder
+ t.references :group
+ t.boolean :can_create
+ t.boolean :can_read
+ t.boolean :can_update
+ t.boolean :can_delete
+ end
+ end
+
+ def self.down
+ drop_table :boxroom_permissions
+ end
+end
diff --git a/db/migrate/20101005071508_create_groups_users.rb b/db/migrate/20101005071508_create_groups_users.rb
new file mode 100644
index 0000000..cc1839f
--- /dev/null
+++ b/db/migrate/20101005071508_create_groups_users.rb
@@ -0,0 +1,12 @@
+class CreateGroupsUsers < ActiveRecord::Migration
+ def self.up
+ create_table :boxroom_groups_users, :id => false do |t|
+ t.references :group
+ t.references :user
+ end
+ end
+
+ def self.down
+ drop_table :boxroom_groups_users
+ end
+end
diff --git a/db/migrate/20110106045148_drop_column_user_id_from_folders.rb b/db/migrate/20110106045148_drop_column_user_id_from_folders.rb
new file mode 100644
index 0000000..6c50a73
--- /dev/null
+++ b/db/migrate/20110106045148_drop_column_user_id_from_folders.rb
@@ -0,0 +1,9 @@
+class DropColumnUserIdFromFolders < ActiveRecord::Migration
+ def self.up
+ remove_column :boxroom_folders, :user_id
+ end
+
+ def self.down
+ add_column :boxroom_folders, :user_id, :integer
+ end
+end
diff --git a/db/migrate/20110106045414_drop_column_user_id_from_user_files.rb b/db/migrate/20110106045414_drop_column_user_id_from_user_files.rb
new file mode 100644
index 0000000..ae392f7
--- /dev/null
+++ b/db/migrate/20110106045414_drop_column_user_id_from_user_files.rb
@@ -0,0 +1,9 @@
+class DropColumnUserIdFromUserFiles < ActiveRecord::Migration
+ def self.up
+ remove_column :boxroom_user_files, :user_id
+ end
+
+ def self.down
+ add_column :boxroom_user_files, :user_id, :integer
+ end
+end
diff --git a/db/migrate/20110529123402_drop_column_access_key_from_users.rb b/db/migrate/20110529123402_drop_column_access_key_from_users.rb
new file mode 100644
index 0000000..37b5fcb
--- /dev/null
+++ b/db/migrate/20110529123402_drop_column_access_key_from_users.rb
@@ -0,0 +1,9 @@
+class DropColumnAccessKeyFromUsers < ActiveRecord::Migration
+ def self.up
+ remove_column :boxroom_users, :access_key
+ end
+
+ def self.down
+ add_column :boxroom_users, :access_key, :string
+ end
+end
diff --git a/db/migrate/20110616215033_create_share_links.rb b/db/migrate/20110616215033_create_share_links.rb
new file mode 100644
index 0000000..70a7c60
--- /dev/null
+++ b/db/migrate/20110616215033_create_share_links.rb
@@ -0,0 +1,15 @@
+class CreateShareLinks < ActiveRecord::Migration
+ def self.up
+ create_table :boxroom_share_links do |t|
+ t.string :emails
+ t.string :link_token
+ t.datetime :link_expires_at
+ t.references :user_file
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :boxroom_share_links
+ end
+end
diff --git a/db/migrate/20120411075110_add_column_signup_token_to_users.rb b/db/migrate/20120411075110_add_column_signup_token_to_users.rb
new file mode 100644
index 0000000..b57069e
--- /dev/null
+++ b/db/migrate/20120411075110_add_column_signup_token_to_users.rb
@@ -0,0 +1,8 @@
+class AddColumnSignupTokenToUsers < ActiveRecord::Migration
+ def change
+ change_table :boxroom_users do |t|
+ t.string :signup_token
+ t.index :signup_token
+ end
+ end
+end
diff --git a/db/migrate/20120411081345_add_column_signup_token_expires_at_to_users.rb b/db/migrate/20120411081345_add_column_signup_token_expires_at_to_users.rb
new file mode 100644
index 0000000..278b561
--- /dev/null
+++ b/db/migrate/20120411081345_add_column_signup_token_expires_at_to_users.rb
@@ -0,0 +1,7 @@
+class AddColumnSignupTokenExpiresAtToUsers < ActiveRecord::Migration
+ def change
+ change_table :boxroom_users do |t|
+ t.datetime :signup_token_expires_at
+ end
+ end
+end
diff --git a/db/migrate/20130307082111_alter_column_type_is_admin_from_users.rb b/db/migrate/20130307082111_alter_column_type_is_admin_from_users.rb
new file mode 100644
index 0000000..786190a
--- /dev/null
+++ b/db/migrate/20130307082111_alter_column_type_is_admin_from_users.rb
@@ -0,0 +1,9 @@
+class AlterColumnTypeIsAdminFromUsers < ActiveRecord::Migration
+ def self.up
+ change_column :boxroom_users, :is_admin, :boolean
+ end
+
+ def self.down
+ change_column :boxroom_users, :is_admin, :string
+ end
+end
diff --git a/db/migrate/20130626210927_add_columns_message_user_id_to_share_links.rb b/db/migrate/20130626210927_add_columns_message_user_id_to_share_links.rb
new file mode 100644
index 0000000..961fd83
--- /dev/null
+++ b/db/migrate/20130626210927_add_columns_message_user_id_to_share_links.rb
@@ -0,0 +1,6 @@
+class AddColumnsMessageUserIdToShareLinks < ActiveRecord::Migration
+ def change
+ add_column :boxroom_share_links, :message, :text
+ add_column :boxroom_share_links, :user_id, :integer
+ end
+end
diff --git a/db/migrate/20130628082245_populate_user_id_in_share_links.rb b/db/migrate/20130628082245_populate_user_id_in_share_links.rb
new file mode 100644
index 0000000..ab9b2b6
--- /dev/null
+++ b/db/migrate/20130628082245_populate_user_id_in_share_links.rb
@@ -0,0 +1,9 @@
+class PopulateUserIdInShareLinks < ActiveRecord::Migration
+ def change
+ active_users = Boxroom::User.where.not(:name => nil)
+
+ if active_users.any? && Boxroom::ShareLink.any?
+ Boxroom::ShareLink.where(:user_id => nil).update_all(:user_id => active_users.first.id)
+ end
+ end
+end
diff --git a/lib/boxroom.rb b/lib/boxroom.rb
new file mode 100644
index 0000000..2defb53
--- /dev/null
+++ b/lib/boxroom.rb
@@ -0,0 +1,24 @@
+require "boxroom/engine"
+require "dynamic_form"
+require 'jquery-fileupload-rails'
+require 'acts_as_tree'
+require 'paperclip'
+require 'boxroom/configuration'
+
+module Boxroom
+ class << self
+ attr_writer :configuration
+
+ def configuration
+ @configuration ||= Configuration.new
+ end
+
+ def reset
+ @configuration = Configuration.new
+ end
+
+ def configure
+ yield(configuration)
+ end
+ end
+end
diff --git a/lib/boxroom/configuration.rb b/lib/boxroom/configuration.rb
new file mode 100644
index 0000000..f78fa9d
--- /dev/null
+++ b/lib/boxroom/configuration.rb
@@ -0,0 +1,10 @@
+module Boxroom
+ class Configuration
+ attr_accessor :site_name, :logo
+
+ def initialize
+ @site_name = 'Boxroom'
+ @logo = 'boxroom/logo.png'
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/boxroom/engine.rb b/lib/boxroom/engine.rb
new file mode 100644
index 0000000..0da685c
--- /dev/null
+++ b/lib/boxroom/engine.rb
@@ -0,0 +1,9 @@
+module Boxroom
+ class Engine < ::Rails::Engine
+ isolate_namespace Boxroom
+
+ initializer 'boxroom.assets.precompile' do |app|
+ app.config.assets.precompile += %w( boxroom/*.png )
+ end
+ end
+end
diff --git a/lib/boxroom/version.rb b/lib/boxroom/version.rb
new file mode 100644
index 0000000..afe6d69
--- /dev/null
+++ b/lib/boxroom/version.rb
@@ -0,0 +1,3 @@
+module Boxroom
+ VERSION = '0.1.0'
+end
diff --git a/lib/tasks/boxroom_tasks.rake b/lib/tasks/boxroom_tasks.rake
new file mode 100644
index 0000000..5ae3d0b
--- /dev/null
+++ b/lib/tasks/boxroom_tasks.rake
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :boxroom do
+# # Task goes here
+# end
diff --git a/test/boxroom_test.rb b/test/boxroom_test.rb
new file mode 100644
index 0000000..6ebfeba
--- /dev/null
+++ b/test/boxroom_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class Boxroom::Test < ActiveSupport::TestCase
+ test "truth" do
+ assert_kind_of Module, Boxroom
+ end
+end
diff --git a/test/dummy/Rakefile b/test/dummy/Rakefile
new file mode 100644
index 0000000..e85f913
--- /dev/null
+++ b/test/dummy/Rakefile
@@ -0,0 +1,6 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require_relative 'config/application'
+
+Rails.application.load_tasks
diff --git a/test/dummy/app/assets/config/manifest.js b/test/dummy/app/assets/config/manifest.js
new file mode 100644
index 0000000..2882d66
--- /dev/null
+++ b/test/dummy/app/assets/config/manifest.js
@@ -0,0 +1,5 @@
+
+//= link_tree ../images
+//= link_directory ../javascripts .js
+//= link_directory ../stylesheets .css
+//= link boxroom_manifest.js
diff --git a/test/dummy/app/assets/images/.keep b/test/dummy/app/assets/images/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/test/dummy/app/assets/javascripts/application.js b/test/dummy/app/assets/javascripts/application.js
new file mode 100644
index 0000000..e54c646
--- /dev/null
+++ b/test/dummy/app/assets/javascripts/application.js
@@ -0,0 +1,13 @@
+// This is a manifest file that'll be compiled into application.js, which will include all the files
+// listed below.
+//
+// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
+// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
+//
+// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
+// compiled file. JavaScript code in this file should be added after the last require_* statement.
+//
+// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
+// about supported directives.
+//
+//= require_tree .
diff --git a/test/dummy/app/assets/javascripts/cable.js b/test/dummy/app/assets/javascripts/cable.js
new file mode 100644
index 0000000..739aa5f
--- /dev/null
+++ b/test/dummy/app/assets/javascripts/cable.js
@@ -0,0 +1,13 @@
+// Action Cable provides the framework to deal with WebSockets in Rails.
+// You can generate new channels where WebSocket features live using the `rails generate channel` command.
+//
+//= require action_cable
+//= require_self
+//= require_tree ./channels
+
+(function() {
+ this.App || (this.App = {});
+
+ App.cable = ActionCable.createConsumer();
+
+}).call(this);
diff --git a/test/dummy/app/assets/javascripts/channels/.keep b/test/dummy/app/assets/javascripts/channels/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/test/dummy/app/assets/stylesheets/application.css b/test/dummy/app/assets/stylesheets/application.css
new file mode 100644
index 0000000..0ebd7fe
--- /dev/null
+++ b/test/dummy/app/assets/stylesheets/application.css
@@ -0,0 +1,15 @@
+/*
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
+ * listed below.
+ *
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
+ *
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
+ * files in this directory. Styles in this file should be added after the last require_* statement.
+ * It is generally better to create a new file per style scope.
+ *
+ *= require_tree .
+ *= require_self
+ */
diff --git a/test/dummy/app/channels/application_cable/channel.rb b/test/dummy/app/channels/application_cable/channel.rb
new file mode 100644
index 0000000..d672697
--- /dev/null
+++ b/test/dummy/app/channels/application_cable/channel.rb
@@ -0,0 +1,4 @@
+module ApplicationCable
+ class Channel < ActionCable::Channel::Base
+ end
+end
diff --git a/test/dummy/app/channels/application_cable/connection.rb b/test/dummy/app/channels/application_cable/connection.rb
new file mode 100644
index 0000000..0ff5442
--- /dev/null
+++ b/test/dummy/app/channels/application_cable/connection.rb
@@ -0,0 +1,4 @@
+module ApplicationCable
+ class Connection < ActionCable::Connection::Base
+ end
+end
diff --git a/test/dummy/app/controllers/application_controller.rb b/test/dummy/app/controllers/application_controller.rb
new file mode 100644
index 0000000..1c07694
--- /dev/null
+++ b/test/dummy/app/controllers/application_controller.rb
@@ -0,0 +1,3 @@
+class ApplicationController < ActionController::Base
+ protect_from_forgery with: :exception
+end
diff --git a/test/dummy/app/controllers/concerns/.keep b/test/dummy/app/controllers/concerns/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/test/dummy/app/helpers/application_helper.rb b/test/dummy/app/helpers/application_helper.rb
new file mode 100644
index 0000000..de6be79
--- /dev/null
+++ b/test/dummy/app/helpers/application_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationHelper
+end
diff --git a/test/dummy/app/jobs/application_job.rb b/test/dummy/app/jobs/application_job.rb
new file mode 100644
index 0000000..a009ace
--- /dev/null
+++ b/test/dummy/app/jobs/application_job.rb
@@ -0,0 +1,2 @@
+class ApplicationJob < ActiveJob::Base
+end
diff --git a/test/dummy/app/mailers/application_mailer.rb b/test/dummy/app/mailers/application_mailer.rb
new file mode 100644
index 0000000..286b223
--- /dev/null
+++ b/test/dummy/app/mailers/application_mailer.rb
@@ -0,0 +1,4 @@
+class ApplicationMailer < ActionMailer::Base
+ default from: 'from@example.com'
+ layout 'mailer'
+end
diff --git a/test/dummy/app/models/application_record.rb b/test/dummy/app/models/application_record.rb
new file mode 100644
index 0000000..10a4cba
--- /dev/null
+++ b/test/dummy/app/models/application_record.rb
@@ -0,0 +1,3 @@
+class ApplicationRecord < ActiveRecord::Base
+ self.abstract_class = true
+end
diff --git a/test/dummy/app/models/concerns/.keep b/test/dummy/app/models/concerns/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/test/dummy/app/views/layouts/application.html.erb b/test/dummy/app/views/layouts/application.html.erb
new file mode 100644
index 0000000..a6eb017
--- /dev/null
+++ b/test/dummy/app/views/layouts/application.html.erb
@@ -0,0 +1,14 @@
+
+
+
+ Dummy
+ <%= csrf_meta_tags %>
+
+ <%= stylesheet_link_tag 'application', media: 'all' %>
+ <%= javascript_include_tag 'application' %>
+
+
+
+ <%= yield %>
+
+
diff --git a/test/dummy/app/views/layouts/mailer.html.erb b/test/dummy/app/views/layouts/mailer.html.erb
new file mode 100644
index 0000000..cbd34d2
--- /dev/null
+++ b/test/dummy/app/views/layouts/mailer.html.erb
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+ <%= yield %>
+
+
diff --git a/test/dummy/app/views/layouts/mailer.text.erb b/test/dummy/app/views/layouts/mailer.text.erb
new file mode 100644
index 0000000..37f0bdd
--- /dev/null
+++ b/test/dummy/app/views/layouts/mailer.text.erb
@@ -0,0 +1 @@
+<%= yield %>
diff --git a/test/dummy/bin/bundle b/test/dummy/bin/bundle
new file mode 100755
index 0000000..66e9889
--- /dev/null
+++ b/test/dummy/bin/bundle
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+load Gem.bin_path('bundler', 'bundle')
diff --git a/test/dummy/bin/rails b/test/dummy/bin/rails
new file mode 100755
index 0000000..0739660
--- /dev/null
+++ b/test/dummy/bin/rails
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+APP_PATH = File.expand_path('../config/application', __dir__)
+require_relative '../config/boot'
+require 'rails/commands'
diff --git a/test/dummy/bin/rake b/test/dummy/bin/rake
new file mode 100755
index 0000000..1724048
--- /dev/null
+++ b/test/dummy/bin/rake
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+require_relative '../config/boot'
+require 'rake'
+Rake.application.run
diff --git a/test/dummy/bin/setup b/test/dummy/bin/setup
new file mode 100755
index 0000000..78c4e86
--- /dev/null
+++ b/test/dummy/bin/setup
@@ -0,0 +1,38 @@
+#!/usr/bin/env ruby
+require 'pathname'
+require 'fileutils'
+include FileUtils
+
+# path to your application root.
+APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
+
+def system!(*args)
+ system(*args) || abort("\n== Command #{args} failed ==")
+end
+
+chdir APP_ROOT do
+ # This script is a starting point to setup your application.
+ # Add necessary setup steps to this file.
+
+ puts '== Installing dependencies =='
+ system! 'gem install bundler --conservative'
+ system('bundle check') || system!('bundle install')
+
+ # Install JavaScript dependencies if using Yarn
+ # system('bin/yarn')
+
+
+ # puts "\n== Copying sample files =="
+ # unless File.exist?('config/database.yml')
+ # cp 'config/database.yml.sample', 'config/database.yml'
+ # end
+
+ puts "\n== Preparing database =="
+ system! 'bin/rails db:setup'
+
+ puts "\n== Removing old logs and tempfiles =="
+ system! 'bin/rails log:clear tmp:clear'
+
+ puts "\n== Restarting application server =="
+ system! 'bin/rails restart'
+end
diff --git a/test/dummy/bin/update b/test/dummy/bin/update
new file mode 100755
index 0000000..a8e4462
--- /dev/null
+++ b/test/dummy/bin/update
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby
+require 'pathname'
+require 'fileutils'
+include FileUtils
+
+# path to your application root.
+APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
+
+def system!(*args)
+ system(*args) || abort("\n== Command #{args} failed ==")
+end
+
+chdir APP_ROOT do
+ # This script is a way to update your development environment automatically.
+ # Add necessary update steps to this file.
+
+ puts '== Installing dependencies =='
+ system! 'gem install bundler --conservative'
+ system('bundle check') || system!('bundle install')
+
+ puts "\n== Updating database =="
+ system! 'bin/rails db:migrate'
+
+ puts "\n== Removing old logs and tempfiles =="
+ system! 'bin/rails log:clear tmp:clear'
+
+ puts "\n== Restarting application server =="
+ system! 'bin/rails restart'
+end
diff --git a/test/dummy/bin/yarn b/test/dummy/bin/yarn
new file mode 100755
index 0000000..c2bacef
--- /dev/null
+++ b/test/dummy/bin/yarn
@@ -0,0 +1,11 @@
+#!/usr/bin/env ruby
+VENDOR_PATH = File.expand_path('..', __dir__)
+Dir.chdir(VENDOR_PATH) do
+ begin
+ exec "yarnpkg #{ARGV.join(" ")}"
+ rescue Errno::ENOENT
+ $stderr.puts "Yarn executable was not detected in the system."
+ $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
+ exit 1
+ end
+end
diff --git a/test/dummy/config.ru b/test/dummy/config.ru
new file mode 100644
index 0000000..f7ba0b5
--- /dev/null
+++ b/test/dummy/config.ru
@@ -0,0 +1,5 @@
+# This file is used by Rack-based servers to start the application.
+
+require_relative 'config/environment'
+
+run Rails.application
diff --git a/test/dummy/config/application.rb b/test/dummy/config/application.rb
new file mode 100644
index 0000000..a18f21c
--- /dev/null
+++ b/test/dummy/config/application.rb
@@ -0,0 +1,18 @@
+require_relative 'boot'
+
+require 'rails/all'
+
+Bundler.require(*Rails.groups)
+require "boxroom"
+
+module Dummy
+ class Application < Rails::Application
+ # Initialize configuration defaults for originally generated Rails version.
+ config.load_defaults 5.1
+
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration should go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded.
+ end
+end
+
diff --git a/test/dummy/config/boot.rb b/test/dummy/config/boot.rb
new file mode 100644
index 0000000..c9aef85
--- /dev/null
+++ b/test/dummy/config/boot.rb
@@ -0,0 +1,5 @@
+# Set up gems listed in the Gemfile.
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
+
+require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
+$LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
diff --git a/test/dummy/config/cable.yml b/test/dummy/config/cable.yml
new file mode 100644
index 0000000..d3dfabd
--- /dev/null
+++ b/test/dummy/config/cable.yml
@@ -0,0 +1,10 @@
+development:
+ adapter: async
+
+test:
+ adapter: async
+
+production:
+ adapter: redis
+ url: redis://localhost:6379/1
+ channel_prefix: dummy_production
diff --git a/test/dummy/config/database.yml b/test/dummy/config/database.yml
new file mode 100644
index 0000000..0d02f24
--- /dev/null
+++ b/test/dummy/config/database.yml
@@ -0,0 +1,25 @@
+# SQLite version 3.x
+# gem install sqlite3
+#
+# Ensure the SQLite 3 gem is defined in your Gemfile
+# gem 'sqlite3'
+#
+default: &default
+ adapter: sqlite3
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+ timeout: 5000
+
+development:
+ <<: *default
+ database: db/development.sqlite3
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ <<: *default
+ database: db/test.sqlite3
+
+production:
+ <<: *default
+ database: db/production.sqlite3
diff --git a/test/dummy/config/environment.rb b/test/dummy/config/environment.rb
new file mode 100644
index 0000000..426333b
--- /dev/null
+++ b/test/dummy/config/environment.rb
@@ -0,0 +1,5 @@
+# Load the Rails application.
+require_relative 'application'
+
+# Initialize the Rails application.
+Rails.application.initialize!
diff --git a/test/dummy/config/environments/development.rb b/test/dummy/config/environments/development.rb
new file mode 100644
index 0000000..55d8c9e
--- /dev/null
+++ b/test/dummy/config/environments/development.rb
@@ -0,0 +1,54 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Do not eager load code on boot.
+ config.eager_load = false
+
+ # Show full error reports.
+ config.consider_all_requests_local = true
+
+ # Enable/disable caching. By default caching is disabled.
+ if Rails.root.join('tmp/caching-dev.txt').exist?
+ config.action_controller.perform_caching = true
+
+ config.cache_store = :memory_store
+ config.public_file_server.headers = {
+ 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}"
+ }
+ else
+ config.action_controller.perform_caching = false
+
+ config.cache_store = :null_store
+ end
+
+ # Don't care if the mailer can't send.
+ config.action_mailer.raise_delivery_errors = false
+
+ config.action_mailer.perform_caching = false
+
+ # Print deprecation notices to the Rails logger.
+ config.active_support.deprecation = :log
+
+ # Raise an error on page load if there are pending migrations.
+ config.active_record.migration_error = :page_load
+
+ # Debug mode disables concatenation and preprocessing of assets.
+ # This option may cause significant delays in view rendering with a large
+ # number of complex assets.
+ config.assets.debug = true
+
+ # Suppress logger output for asset requests.
+ config.assets.quiet = true
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+
+ # Use an evented file watcher to asynchronously detect changes in source code,
+ # routes, locales, etc. This feature depends on the listen gem.
+ # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+end
diff --git a/test/dummy/config/environments/production.rb b/test/dummy/config/environments/production.rb
new file mode 100644
index 0000000..6ea0018
--- /dev/null
+++ b/test/dummy/config/environments/production.rb
@@ -0,0 +1,91 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # Code is not reloaded between requests.
+ config.cache_classes = true
+
+ # Eager load code on boot. This eager loads most of Rails and
+ # your application in memory, allowing both threaded web servers
+ # and those relying on copy on write to perform better.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
+ # Full error reports are disabled and caching is turned on.
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Attempt to read encrypted secrets from `config/secrets.yml.enc`.
+ # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or
+ # `config/secrets.yml.key`.
+ config.read_encrypted_secrets = true
+
+ # Disable serving static files from the `/public` folder by default since
+ # Apache or NGINX already handles this.
+ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
+
+ # Compress JavaScripts and CSS.
+ config.assets.js_compressor = :uglifier
+ # config.assets.css_compressor = :sass
+
+ # Do not fallback to assets pipeline if a precompiled asset is missed.
+ config.assets.compile = false
+
+ # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
+ # config.action_controller.asset_host = 'http://assets.example.com'
+
+ # Specifies the header that your server uses for sending files.
+ # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+
+ # Mount Action Cable outside main process or domain
+ # config.action_cable.mount_path = nil
+ # config.action_cable.url = 'wss://example.com/cable'
+ # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # Use the lowest log level to ensure availability of diagnostic information
+ # when problems arise.
+ config.log_level = :debug
+
+ # Prepend all log lines with the following tags.
+ config.log_tags = [ :request_id ]
+
+ # Use a different cache store in production.
+ # config.cache_store = :mem_cache_store
+
+ # Use a real queuing backend for Active Job (and separate queues per environment)
+ # config.active_job.queue_adapter = :resque
+ # config.active_job.queue_name_prefix = "dummy_#{Rails.env}"
+ config.action_mailer.perform_caching = false
+
+ # Ignore bad email addresses and do not raise email delivery errors.
+ # Set this to true and configure the email server for immediate delivery to raise delivery errors.
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation cannot be found).
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners.
+ config.active_support.deprecation = :notify
+
+ # Use default logging formatter so that PID and timestamp are not suppressed.
+ config.log_formatter = ::Logger::Formatter.new
+
+ # Use a different logger for distributed setups.
+ # require 'syslog/logger'
+ # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
+
+ if ENV["RAILS_LOG_TO_STDOUT"].present?
+ logger = ActiveSupport::Logger.new(STDOUT)
+ logger.formatter = config.log_formatter
+ config.logger = ActiveSupport::TaggedLogging.new(logger)
+ end
+
+ # Do not dump schema after migrations.
+ config.active_record.dump_schema_after_migration = false
+end
diff --git a/test/dummy/config/environments/test.rb b/test/dummy/config/environments/test.rb
new file mode 100644
index 0000000..8e5cbde
--- /dev/null
+++ b/test/dummy/config/environments/test.rb
@@ -0,0 +1,42 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # The test environment is used exclusively to run your application's
+ # test suite. You never need to work with it otherwise. Remember that
+ # your test database is "scratch space" for the test suite and is wiped
+ # and recreated between test runs. Don't rely on the data there!
+ config.cache_classes = true
+
+ # Do not eager load code on boot. This avoids loading your whole application
+ # just for the purpose of running a single test. If you are using a tool that
+ # preloads Rails for running tests, you may have to set it to true.
+ config.eager_load = false
+
+ # Configure public file server for tests with Cache-Control for performance.
+ config.public_file_server.enabled = true
+ config.public_file_server.headers = {
+ 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}"
+ }
+
+ # Show full error reports and disable caching.
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+
+ # Raise exceptions instead of rendering exception templates.
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment.
+ config.action_controller.allow_forgery_protection = false
+ config.action_mailer.perform_caching = false
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+
+ # Print deprecation notices to the stderr.
+ config.active_support.deprecation = :stderr
+
+ # Raises error for missing translations
+ # config.action_view.raise_on_missing_translations = true
+end
diff --git a/test/dummy/config/initializers/application_controller_renderer.rb b/test/dummy/config/initializers/application_controller_renderer.rb
new file mode 100644
index 0000000..89d2efa
--- /dev/null
+++ b/test/dummy/config/initializers/application_controller_renderer.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+# ActiveSupport::Reloader.to_prepare do
+# ApplicationController.renderer.defaults.merge!(
+# http_host: 'example.org',
+# https: false
+# )
+# end
diff --git a/test/dummy/config/initializers/assets.rb b/test/dummy/config/initializers/assets.rb
new file mode 100644
index 0000000..4b828e8
--- /dev/null
+++ b/test/dummy/config/initializers/assets.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+
+# Version of your assets, change this if you want to expire all your assets.
+Rails.application.config.assets.version = '1.0'
+
+# Add additional assets to the asset load path.
+# Rails.application.config.assets.paths << Emoji.images_path
+# Add Yarn node_modules folder to the asset load path.
+Rails.application.config.assets.paths << Rails.root.join('node_modules')
+
+# Precompile additional assets.
+# application.js, application.css, and all non-JS/CSS in the app/assets
+# folder are already added.
+# Rails.application.config.assets.precompile += %w( admin.js admin.css )
diff --git a/test/dummy/config/initializers/backtrace_silencers.rb b/test/dummy/config/initializers/backtrace_silencers.rb
new file mode 100644
index 0000000..59385cd
--- /dev/null
+++ b/test/dummy/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
diff --git a/test/dummy/config/initializers/cookies_serializer.rb b/test/dummy/config/initializers/cookies_serializer.rb
new file mode 100644
index 0000000..5a6a32d
--- /dev/null
+++ b/test/dummy/config/initializers/cookies_serializer.rb
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Specify a serializer for the signed and encrypted cookie jars.
+# Valid options are :json, :marshal, and :hybrid.
+Rails.application.config.action_dispatch.cookies_serializer = :json
diff --git a/test/dummy/config/initializers/filter_parameter_logging.rb b/test/dummy/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 0000000..4a994e1
--- /dev/null
+++ b/test/dummy/config/initializers/filter_parameter_logging.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure sensitive parameters which will be filtered from the log file.
+Rails.application.config.filter_parameters += [:password]
diff --git a/test/dummy/config/initializers/inflections.rb b/test/dummy/config/initializers/inflections.rb
new file mode 100644
index 0000000..ac033bf
--- /dev/null
+++ b/test/dummy/config/initializers/inflections.rb
@@ -0,0 +1,16 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format. Inflections
+# are locale specific, and you may define rules for as many different
+# locales as you wish. All of these examples are active by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
+
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.acronym 'RESTful'
+# end
diff --git a/test/dummy/config/initializers/mime_types.rb b/test/dummy/config/initializers/mime_types.rb
new file mode 100644
index 0000000..dc18996
--- /dev/null
+++ b/test/dummy/config/initializers/mime_types.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
diff --git a/test/dummy/config/initializers/wrap_parameters.rb b/test/dummy/config/initializers/wrap_parameters.rb
new file mode 100644
index 0000000..bbfc396
--- /dev/null
+++ b/test/dummy/config/initializers/wrap_parameters.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters format: [:json]
+end
+
+# To enable root element in JSON for ActiveRecord objects.
+# ActiveSupport.on_load(:active_record) do
+# self.include_root_in_json = true
+# end
diff --git a/test/dummy/config/locales/en.yml b/test/dummy/config/locales/en.yml
new file mode 100644
index 0000000..decc5a8
--- /dev/null
+++ b/test/dummy/config/locales/en.yml
@@ -0,0 +1,33 @@
+# Files in the config/locales directory are used for internationalization
+# and are automatically loaded by Rails. If you want to use locales other
+# than English, add the necessary files in this directory.
+#
+# To use the locales, use `I18n.t`:
+#
+# I18n.t 'hello'
+#
+# In views, this is aliased to just `t`:
+#
+# <%= t('hello') %>
+#
+# To use a different locale, set it with `I18n.locale`:
+#
+# I18n.locale = :es
+#
+# This would use the information in config/locales/es.yml.
+#
+# The following keys must be escaped otherwise they will not be retrieved by
+# the default I18n backend:
+#
+# true, false, on, off, yes, no
+#
+# Instead, surround them with single quotes.
+#
+# en:
+# 'true': 'foo'
+#
+# To learn more, please read the Rails Internationalization guide
+# available at http://guides.rubyonrails.org/i18n.html.
+
+en:
+ hello: "Hello world"
diff --git a/test/dummy/config/puma.rb b/test/dummy/config/puma.rb
new file mode 100644
index 0000000..1e19380
--- /dev/null
+++ b/test/dummy/config/puma.rb
@@ -0,0 +1,56 @@
+# Puma can serve each request in a thread from an internal thread pool.
+# The `threads` method setting takes two numbers: a minimum and maximum.
+# Any libraries that use thread pools should be configured to match
+# the maximum value specified for Puma. Default is set to 5 threads for minimum
+# and maximum; this matches the default thread size of Active Record.
+#
+threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
+threads threads_count, threads_count
+
+# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
+#
+port ENV.fetch("PORT") { 3000 }
+
+# Specifies the `environment` that Puma will run in.
+#
+environment ENV.fetch("RAILS_ENV") { "development" }
+
+# Specifies the number of `workers` to boot in clustered mode.
+# Workers are forked webserver processes. If using threads and workers together
+# the concurrency of the application would be max `threads` * `workers`.
+# Workers do not work on JRuby or Windows (both of which do not support
+# processes).
+#
+# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
+
+# Use the `preload_app!` method when specifying a `workers` number.
+# This directive tells Puma to first boot the application and load code
+# before forking the application. This takes advantage of Copy On Write
+# process behavior so workers use less memory. If you use this option
+# you need to make sure to reconnect any threads in the `on_worker_boot`
+# block.
+#
+# preload_app!
+
+# If you are preloading your application and using Active Record, it's
+# recommended that you close any connections to the database before workers
+# are forked to prevent connection leakage.
+#
+# before_fork do
+# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)
+# end
+
+# The code in the `on_worker_boot` will be called if you are using
+# clustered mode by specifying a number of `workers`. After each worker
+# process is booted, this block will be run. If you are using the `preload_app!`
+# option, you will want to use this block to reconnect to any threads
+# or connections that may have been created at application boot, as Ruby
+# cannot share connections between processes.
+#
+# on_worker_boot do
+# ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
+# end
+#
+
+# Allow puma to be restarted by `rails restart` command.
+plugin :tmp_restart
diff --git a/test/dummy/config/routes.rb b/test/dummy/config/routes.rb
new file mode 100644
index 0000000..72926de
--- /dev/null
+++ b/test/dummy/config/routes.rb
@@ -0,0 +1,3 @@
+Rails.application.routes.draw do
+ mount Boxroom::Engine => "/boxroom"
+end
diff --git a/test/dummy/config/secrets.yml b/test/dummy/config/secrets.yml
new file mode 100644
index 0000000..fc63416
--- /dev/null
+++ b/test/dummy/config/secrets.yml
@@ -0,0 +1,32 @@
+# Be sure to restart your server when you modify this file.
+
+# Your secret key is used for verifying the integrity of signed cookies.
+# If you change this key, all old signed cookies will become invalid!
+
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+# You can use `rails secret` to generate a secure secret key.
+
+# Make sure the secrets in this file are kept private
+# if you're sharing your code publicly.
+
+# Shared secrets are available across all environments.
+
+# shared:
+# api_key: a1B2c3D4e5F6
+
+# Environmental secrets are only available for that specific environment.
+
+development:
+ secret_key_base: 78494ed70673753c6f483e51ec3d207f9815f32a815f8a5e22ccb19d93973b28b1cbbf636e0bfe1f42430fc2e09293839e9bae1747ad8bad6ee84a062c08e03a
+
+test:
+ secret_key_base: 391840a9588b36699c9f3bf7077da743159cf67bf8424e70d2e6948204408d1a3869f3b80844c799d0372ef9eb57381333f8145db9f99f0fe210ee53ebd71b5d
+
+# Do not keep production secrets in the unencrypted secrets file.
+# Instead, either read values from the environment.
+# Or, use `bin/rails secrets:setup` to configure encrypted secrets
+# and move the `production:` environment over there.
+
+production:
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
diff --git a/test/dummy/config/spring.rb b/test/dummy/config/spring.rb
new file mode 100644
index 0000000..c9119b4
--- /dev/null
+++ b/test/dummy/config/spring.rb
@@ -0,0 +1,6 @@
+%w(
+ .ruby-version
+ .rbenv-vars
+ tmp/restart.txt
+ tmp/caching-dev.txt
+).each { |path| Spring.watch(path) }
diff --git a/test/dummy/lib/assets/.keep b/test/dummy/lib/assets/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/test/dummy/log/.keep b/test/dummy/log/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/test/dummy/package.json b/test/dummy/package.json
new file mode 100644
index 0000000..caa2d7b
--- /dev/null
+++ b/test/dummy/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "dummy",
+ "private": true,
+ "dependencies": {}
+}
diff --git a/test/dummy/public/404.html b/test/dummy/public/404.html
new file mode 100644
index 0000000..2be3af2
--- /dev/null
+++ b/test/dummy/public/404.html
@@ -0,0 +1,67 @@
+
+
+
+ The page you were looking for doesn't exist (404)
+
+
+
+
+
+
+
+
+
The page you were looking for doesn't exist.
+
You may have mistyped the address or the page may have moved.
+
+
If you are the application owner check the logs for more information.
+
+
+
diff --git a/test/dummy/public/422.html b/test/dummy/public/422.html
new file mode 100644
index 0000000..c08eac0
--- /dev/null
+++ b/test/dummy/public/422.html
@@ -0,0 +1,67 @@
+
+
+
+ The change you wanted was rejected (422)
+
+
+
+
+
+
+
+
+
The change you wanted was rejected.
+
Maybe you tried to change something you didn't have access to.
+
+
If you are the application owner check the logs for more information.
+
+
+
diff --git a/test/dummy/public/500.html b/test/dummy/public/500.html
new file mode 100644
index 0000000..78a030a
--- /dev/null
+++ b/test/dummy/public/500.html
@@ -0,0 +1,66 @@
+
+
+
+ We're sorry, but something went wrong (500)
+
+
+
+
+
+
+
+
+
We're sorry, but something went wrong.
+
+
If you are the application owner check the logs for more information.
+
+
+
diff --git a/test/dummy/public/apple-touch-icon-precomposed.png b/test/dummy/public/apple-touch-icon-precomposed.png
new file mode 100644
index 0000000..e69de29
diff --git a/test/dummy/public/apple-touch-icon.png b/test/dummy/public/apple-touch-icon.png
new file mode 100644
index 0000000..e69de29
diff --git a/test/dummy/public/favicon.ico b/test/dummy/public/favicon.ico
new file mode 100644
index 0000000..e69de29
diff --git a/test/factories.rb b/test/factories.rb
new file mode 100644
index 0000000..37224b5
--- /dev/null
+++ b/test/factories.rb
@@ -0,0 +1,45 @@
+FactoryGirl.define do
+ factory :folder do
+ sequence(:name) { |i| "test#{i}" }
+ parent { Folder.where(:name => 'Root folder').first_or_create }
+ end
+end
+
+FactoryGirl.define do
+ factory :group do
+ sequence(:name) { |i| "test#{i}" }
+ end
+end
+
+FactoryGirl.define do
+ factory :share_link do
+ emails 'email1@domain.com, email2@domain.com'
+ link_expires_at { 2.weeks.from_now.end_of_day }
+ end
+end
+
+FactoryGirl.define do
+ factory :user_file do
+ attachment { fixture_file }
+ sequence(:attachment_file_name) { |i| "test#{i}.txt" }
+ folder { Folder.where(:name => 'Root folder').first_or_create }
+ end
+end
+
+FactoryGirl.define do
+ factory :user do
+ sequence(:name) { |i| "test#{i}" }
+ sequence(:email) { |i| "test#{i}@test.com" }
+ password 'secret123'
+ password_confirmation { |u| u.password }
+ password_required true
+ reset_password_token ''
+ dont_clear_reset_password_token false
+ remember_token ''
+ is_admin false
+ end
+end
+
+def fixture_file
+ File.open("#{Rails.root}/test/fixtures/textfile.txt")
+end
diff --git a/test/fixtures/textfile.txt b/test/fixtures/textfile.txt
new file mode 100644
index 0000000..9daeafb
--- /dev/null
+++ b/test/fixtures/textfile.txt
@@ -0,0 +1 @@
+test
diff --git a/test/integration/navigation_test.rb b/test/integration/navigation_test.rb
new file mode 100644
index 0000000..f5d1ec2
--- /dev/null
+++ b/test/integration/navigation_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class NavigationTest < ActionDispatch::IntegrationTest
+ # test "the truth" do
+ # assert true
+ # end
+end
+
diff --git a/test/test_helper.rb b/test/test_helper.rb
new file mode 100644
index 0000000..5a85c6c
--- /dev/null
+++ b/test/test_helper.rb
@@ -0,0 +1,29 @@
+require File.expand_path("../../test/dummy/config/environment.rb", __FILE__)
+ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../test/dummy/db/migrate", __FILE__)]
+ActiveRecord::Migrator.migrations_paths << File.expand_path('../../db/migrate', __FILE__)
+require "rails/test_help"
+
+# Filter out Minitest backtrace while allowing backtrace from other libraries
+# to be shown.
+Minitest.backtrace_filter = Minitest::BacktraceFilter.new
+
+
+# Load fixtures from the engine
+if ActiveSupport::TestCase.respond_to?(:fixture_path=)
+ ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
+ ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
+ ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
+ ActiveSupport::TestCase.fixtures :all
+end
+
+# ENV["RAILS_ENV"] = "test"
+# require File.expand_path('../../config/environment', __FILE__)
+# require 'rails/test_help'
+#
+# class ActiveSupport::TestCase
+# include FactoryGirl::Syntax::Methods
+#
+# def clear_root_folder
+# Folder.instance_variable_set('@root_folder', nil)
+# end
+# end
diff --git a/test/unit/clipboard_test.rb b/test/unit/clipboard_test.rb
new file mode 100644
index 0000000..f397150
--- /dev/null
+++ b/test/unit/clipboard_test.rb
@@ -0,0 +1,134 @@
+require 'test_helper'
+
+class ClipboardTest < ActiveSupport::TestCase
+ test 'a folder can be added to the clipboard' do
+ folder = create(:folder)
+ clipboard = Clipboard.new
+ assert clipboard.folders.empty?
+ assert clipboard.empty?
+
+ clipboard.add(folder)
+ assert_equal clipboard.folders.count, 1
+ assert !clipboard.empty?
+ end
+
+ test 'a file can be added to the clipboard' do
+ file = create(:user_file)
+ clipboard = Clipboard.new
+ assert clipboard.files.empty?
+ assert clipboard.empty?
+
+ clipboard.add(file)
+ assert_equal clipboard.files.count, 1
+ assert !clipboard.empty?
+ end
+
+ test 'a folder can be removed from the clipboard' do
+ folder = create(:folder)
+ clipboard = Clipboard.new
+ clipboard.add(folder)
+ assert_equal clipboard.folders.count, 1
+ assert !clipboard.empty?
+
+ clipboard.remove(folder)
+ assert clipboard.folders.empty?
+ assert clipboard.empty?
+ end
+
+ test 'a file can be removed from the clipboard' do
+ file = create(:user_file)
+ clipboard = Clipboard.new
+ clipboard.add(file)
+ assert_equal clipboard.files.count, 1
+ assert !clipboard.empty?
+
+ clipboard.remove(file)
+ assert clipboard.files.empty?
+ assert clipboard.empty?
+ end
+
+ test 'the same folder cannot be added twice' do
+ folder = create(:folder)
+ clipboard = Clipboard.new
+ clipboard.add(folder)
+ assert_equal clipboard.folders.count, 1
+
+ another_folder = create(:folder)
+ clipboard.add(another_folder)
+ assert_equal clipboard.folders.count, 2
+
+ clipboard.add(folder)
+ assert_equal clipboard.folders.count, 2
+ end
+
+ test 'the same file cannot be added twice' do
+ file = create(:user_file)
+ clipboard = Clipboard.new
+ clipboard.add(file)
+ assert_equal clipboard.files.count, 1
+
+ another_file = create(:user_file)
+ clipboard.add(another_file)
+ assert_equal clipboard.files.count, 2
+
+ clipboard.add(file)
+ assert_equal clipboard.files.count, 2
+ end
+
+ test 'when a folder is updated the referenced folder on the clipboard must also change' do
+ folder = create(:folder, :name => 'Test')
+ clipboard = Clipboard.new
+ clipboard.add(folder)
+ assert_equal clipboard.folders.first.name, 'Test'
+
+ folder.update_attributes(:name => 'Name changed')
+ assert_equal clipboard.folders.first.name, 'Name changed'
+ end
+
+ test 'when a file is updated the referenced file on the clipboard must also change' do
+ file = create(:user_file)
+ clipboard = Clipboard.new
+ clipboard.add(file)
+ assert_not_equal clipboard.files.first.attachment_file_name, 'Name changed.txt'
+
+ file.update_attributes(:attachment_file_name => 'Name changed.txt')
+ assert_equal clipboard.files.first.attachment_file_name, 'Name changed.txt'
+ end
+
+ test 'a deleted folder must also be deleted from the clipboard' do
+ folder = create(:folder)
+ clipboard = Clipboard.new
+ clipboard.add(folder)
+ assert !clipboard.folders.empty?
+
+ folder.destroy
+ assert clipboard.empty?
+ assert clipboard.folders.empty?
+ end
+
+ test 'a deleted file must also be deleted from the clipboard' do
+ file = create(:user_file)
+ clipboard = Clipboard.new
+ clipboard.add(file)
+ assert !clipboard.files.empty?
+
+ file.destroy
+ assert clipboard.empty?
+ assert clipboard.files.empty?
+ end
+
+ test 'reset clears all the files and folders from the clipboard' do
+ clipboard = Clipboard.new
+ 3.times { clipboard.add(create(:user_file)) }
+ 3.times { clipboard.add(create(:folder)) }
+
+ assert_equal clipboard.files.size, 3
+ assert_equal clipboard.folders.size, 3
+ assert !clipboard.empty?
+
+ clipboard.reset
+ assert clipboard.files.empty?
+ assert clipboard.folders.empty?
+ assert clipboard.empty?
+ end
+end
diff --git a/test/unit/folder_test.rb b/test/unit/folder_test.rb
new file mode 100644
index 0000000..135b2ee
--- /dev/null
+++ b/test/unit/folder_test.rb
@@ -0,0 +1,207 @@
+require 'test_helper'
+
+class FolderTest < ActiveSupport::TestCase
+ def setup
+ clear_root_folder
+ end
+
+ test 'dependent files get deleted' do
+ folder1 = create(:folder)
+ assert_equal Folder.all.count, 2 # Root folder gets created automatically
+
+ 3.times { create(:user_file, :folder => folder1) }
+ assert_equal folder1.user_files.count, 3
+
+ folder2 = create(:folder)
+ assert_equal Folder.all.count, 3
+
+ 5.times { create(:user_file, :folder => folder2) }
+ assert_equal folder2.user_files.count, 5
+ assert_equal UserFile.all.count, 8
+
+ folder1.destroy
+ assert_equal UserFile.all.count, 5
+
+ folder2.destroy
+ assert_equal UserFile.all.count, 0
+ end
+
+ test 'dependent permissions get deleted' do
+ root = create(:folder, :name => 'Root folder', :parent => nil) # Root folder
+ assert_equal Folder.all.count, 1
+
+ 3.times { create(:group) }
+ assert Group.all.count > 0
+ assert_equal root.permissions.count, Group.all.count
+
+ folder1 = create(:folder)
+ folder2 = create(:folder)
+ assert_equal folder1.permissions.count, 3
+ assert_equal folder2.permissions.count, 3
+ assert_equal Permission.all.count, 9
+
+ folder1.destroy
+ assert_equal Permission.all.count, 6
+
+ folder2.destroy
+ assert_equal Permission.all.count, 3
+ end
+
+ test 'name is unique' do
+ folder = create(:folder, :name => 'Test')
+ assert Folder.exists?(:name => 'Test')
+
+ folder2 = Folder.new(:name => 'Test')
+ folder2.parent = folder
+ assert folder2.save
+
+ folder3 = Folder.new(:name => 'Test')
+ folder3.parent = folder
+ assert !folder3.save
+ end
+
+ test 'name is not empty' do
+ folder = Folder.new
+ assert !folder.save
+ end
+
+ test 'cannot create a folder without a parent' do
+ folder = Folder.new(:name => 'Test')
+ assert_nil folder.parent
+ assert_raise(RuntimeError) { folder.save }
+ end
+
+ test 'permissions get created' do
+ root = create(:folder, :name => 'Root folder', :parent => nil) # Root folder
+ assert_equal Folder.all.count, 1
+
+ create(:group)
+ assert Group.all.count > 0
+ assert_equal root.permissions.count, Group.all.count
+
+ root.permissions.each do |permission|
+ assert !permission.can_create
+ assert permission.can_read
+ assert !permission.can_update
+ assert !permission.can_delete
+
+ # Change the permissions
+ permission.update_attributes(:can_create => true, :can_update => true)
+ end
+
+ folder = create(:folder)
+ assert_equal Folder.all.count, 2
+ assert_equal folder.permissions.count, Group.all.count
+
+ # Test if updated permissions get copied correctly
+ folder.permissions.each do |permission|
+ assert permission.can_create
+ assert permission.can_read
+ assert permission.can_update
+ assert !permission.can_delete
+ end
+ end
+
+ test 'cannot delete root folder' do
+ folder = create(:folder)
+ root = Folder.root
+
+ assert_raise(RuntimeError) { root.destroy }
+ assert folder.destroy
+ end
+
+ test 'cannot copy a folder to anything other than a folder' do
+ file = create(:user_file)
+ folder1 = create(:folder)
+ folder2 = create(:folder)
+
+ assert_raise(RuntimeError) { folder1.copy(nil) }
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { folder1.copy('A string...') }
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { folder1.copy(file) }
+ assert folder1.copy(folder2)
+ end
+
+ test 'copy a folder' do
+ folder1 = create(:folder)
+ 5.times { create(:user_file, :folder => folder1) }
+
+ folder2 = create(:folder)
+ 3.times { create(:user_file, :folder => folder2) }
+
+ assert_raise(ActiveRecord::RecordInvalid) { folder1.copy(Folder.root) }
+ assert_equal UserFile.all.count, 8
+ assert_equal folder2.children.count, 0
+ assert folder1.copy(folder2)
+ assert_equal UserFile.all.count, 13
+ assert_equal folder2.children.count, 1
+ end
+
+ test 'cannot move a folder to anything other than a folder' do
+ file = create(:user_file)
+ folder1 = create(:folder)
+ folder2 = create(:folder)
+
+ assert_raise(RuntimeError) { folder1.move(nil) }
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { folder1.move('A string...') }
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { folder1.move(file) }
+ assert folder1.move(folder2)
+ end
+
+ test 'move a folder' do
+ folder1 = create(:folder)
+ folder2 = create(:folder, :parent => folder1)
+ folder3 = create(:folder)
+
+ assert folder1.parent_of?(folder2)
+ assert !folder1.parent_of?(folder3)
+
+ # Should not be able to move a folder to its own sub-folder
+ assert_raise(RuntimeError) { folder1.move(folder2) }
+
+ assert_equal Folder.all.count, 4
+ assert_equal folder1.parent, Folder.root
+
+ folder1.move(folder3)
+ assert_equal folder1.parent, folder3
+ assert_equal Folder.all.count, 4
+
+ assert folder3.parent_of?(folder1)
+ assert folder3.parent_of?(folder2)
+ end
+
+ test 'whether a folder is root or not' do
+ folder1 = create(:folder)
+ assert !folder1.is_root?
+
+ folder2 = Folder.new
+ assert !folder2.is_root?
+
+ root = Folder.root
+ assert root.is_root?
+ end
+
+ test 'whether a folder has children or not' do
+ folder = create(:folder)
+ assert !folder.has_children?
+
+ root = Folder.root
+ assert_equal folder.parent, root
+ assert root.has_children?
+
+ folder2 = create(:folder, :parent => folder)
+ assert folder.has_children?
+
+ folder.destroy
+ assert !root.has_children?
+ end
+
+ test 'that the root folder is really the root folder' do
+ folder = create(:folder)
+ assert !folder.is_root?
+
+ root = Folder.root
+ assert root.is_root?
+ assert_equal root.name, 'Root folder'
+ assert_nil root.parent
+ end
+end
diff --git a/test/unit/group_test.rb b/test/unit/group_test.rb
new file mode 100644
index 0000000..fa786fa
--- /dev/null
+++ b/test/unit/group_test.rb
@@ -0,0 +1,72 @@
+require 'test_helper'
+
+class GroupTest < ActiveSupport::TestCase
+ test 'dependent permissions get deleted' do
+ 3.times { create(:folder) }
+ assert_equal Folder.all.count, 4 # Root folder gets created automatically
+
+ group1 = create(:group)
+ group2 = create(:group)
+ assert_equal group1.permissions.count, 4
+ assert_equal group2.permissions.count, 4
+ assert_equal Permission.all.count, 8
+
+ group1.destroy
+ assert_equal Permission.all.count, 4
+
+ group2.destroy
+ assert_equal Permission.all.count, 0
+ end
+
+ test 'name is unique' do
+ create(:group, :name => 'Users')
+ assert Group.exists?(:name => 'Users')
+
+ group = Group.new(:name => 'Users')
+ assert !group.save
+ end
+
+ test 'name is not empty' do
+ group = Group.new
+ assert !group.save
+ end
+
+ test 'admin permissions get created' do
+ create(:folder)
+ assert Folder.all.count > 0
+
+ group = create(:group, :name => 'Admins')
+ assert_equal group.permissions.count, Folder.all.count
+
+ group.permissions.each do |permission|
+ assert permission.can_create
+ assert permission.can_read
+ assert permission.can_update
+ assert permission.can_delete
+ end
+ end
+
+ test 'permissions get created' do
+ create(:folder)
+ assert Folder.all.count > 0
+
+ group = create(:group)
+ assert_equal group.permissions.count, Folder.all.count
+
+ group.permissions.each do |permission|
+ assert !permission.can_create
+ assert_equal permission.can_read, permission.folder.is_root?
+ assert !permission.can_update
+ assert !permission.can_delete
+ end
+ end
+
+ test 'cannot delete admins group' do
+ admins = create(:group, :name => 'Admins')
+ normal_group = create(:group)
+
+ assert admins.admins_group?
+ assert_raise(RuntimeError) { admins.destroy }
+ assert normal_group.destroy
+ end
+end
diff --git a/test/unit/share_link_test.rb b/test/unit/share_link_test.rb
new file mode 100644
index 0000000..f815f2d
--- /dev/null
+++ b/test/unit/share_link_test.rb
@@ -0,0 +1,78 @@
+require 'test_helper'
+
+class ShareLinkTest < ActiveSupport::TestCase
+ test 'emails is not empty' do
+ assert_raise(ActiveRecord::RecordInvalid) { create(:share_link, :emails => '') }
+ end
+
+ test 'link expires at is not empty' do
+ assert_raise(ActiveRecord::RecordInvalid) { create(:share_link, :link_expires_at => '') }
+ end
+
+ test 'emails is not longer than 255 characters' do
+ share_link = create(:share_link)
+
+ # 255 chars
+ share_link.emails = 'email@domain.com, another-email@domain.com, email@domain.com, another-email@domain.com, email@domain.com, another-email@domain.com, email@domain.com, another-email@domain.com, email@domain.com, anotheremail@domain.com, email@domain.com, another@domain.com'
+ assert share_link.save
+
+ # 256 chars
+ share_link.emails = 'email@domain.com, another-email@domain.com, email@domain.com, another-email@domain.com, email@domain.com, another-email@domain.com, email@domain.com, another-email@domain.com, email@domain.com, anotheremail@domain.com, email@domain.com, anothere@domain.com'
+ assert !share_link.save
+ end
+
+ test 'the format of emails is valid' do
+ assert_raise(ActiveRecord::RecordInvalid) { create(:share_link, :emails => 'mail@domain.com, @.com') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:share_link, :emails => 'mail@domain.com, @test.com') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:share_link, :emails => 'mail@domain.com, test@.com, another-email@domain.com') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:share_link, :emails => 'mail@domain.com, test@test.') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:share_link, :emails => 'mail@domain.com, test@$%^.com') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:share_link, :emails => 'mail@domain.com, test@test.c') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:share_link, :emails => 'mail@domain.com, test@test.$$$') }
+ end
+
+ test 'a link token gets generated' do
+ share_link1 = create(:share_link)
+ assert !share_link1.link_token.blank?
+
+ share_link2 = ShareLink.new
+ share_link2.user_file = create(:user_file)
+ share_link2.emails = 'mail@domain.com'
+ share_link2.link_expires_at = 2.weeks.from_now.end_of_day
+ assert share_link2.link_token.blank?
+
+ share_link2.save
+ assert !share_link2.link_token.blank?
+ end
+
+ test 'active share links' do
+ expiry_dates = [1.week.ago, 1.week.from_now, 2.weeks.from_now, 3.weeks.from_now, 1.month.from_now]
+
+ expiry_dates.each do |expiry_date|
+ create(:share_link, :link_expires_at => expiry_date)
+ end
+
+ assert_equal ShareLink.active_share_links.count, 4
+ end
+
+ test 'the correct file is returned for a given link token' do
+ user_file = create(:user_file)
+
+ share_link1 = create(:share_link)
+ share_link1.user_file = user_file
+ share_link1.save
+
+ share_link2 = create(:share_link, :link_expires_at => 1.week.ago.end_of_day)
+ share_link2.user_file = user_file
+ share_link2.save
+
+ random_token = SecureRandom.hex(10)
+ assert_raise(NoMethodError) { ShareLink.file_for_token(random_token) }
+
+ old_link_token = share_link2.link_token
+ assert_raise(RuntimeError) { ShareLink.file_for_token(old_link_token) }
+
+ valid_link_token = share_link1.link_token
+ assert_equal ShareLink.file_for_token(valid_link_token), user_file
+ end
+end
diff --git a/test/unit/user_file_test.rb b/test/unit/user_file_test.rb
new file mode 100644
index 0000000..292c19e
--- /dev/null
+++ b/test/unit/user_file_test.rb
@@ -0,0 +1,137 @@
+require 'test_helper'
+
+class UserFileTest < ActiveSupport::TestCase
+ def setup
+ clear_root_folder
+ end
+
+ test 'dependent share links get deleted' do
+ file1 = create(:user_file)
+ assert_equal UserFile.all.count, 1
+
+ 3.times { create(:share_link, :user_file => file1) }
+ assert_equal file1.share_links.count, 3
+
+ file2 = create(:user_file)
+ assert_equal UserFile.all.count, 2
+
+ 5.times { create(:share_link, :user_file => file2) }
+ assert_equal file2.share_links.count, 5
+ assert_equal ShareLink.all.count, 8
+
+ file1.destroy
+ assert_equal ShareLink.all.count, 5
+
+ file2.destroy
+ assert_equal ShareLink.all.count, 0
+ end
+
+ test 'attachment is not empty' do
+ folder = create(:folder)
+ file = folder.user_files.build
+ assert !file.save
+
+ file.attachment = fixture_file
+ assert file.save
+ end
+
+ test 'folder is not empty' do
+ file = UserFile.new(:attachment => fixture_file)
+ assert !file.save
+
+ folder = create(:folder)
+ file.folder = folder
+ assert file.save
+ end
+
+ test 'attachment file name is unique' do
+ file = create(:user_file)
+ file.update_attributes(:attachment_file_name => 'Test.txt')
+ assert UserFile.exists?(:attachment_file_name => 'Test.txt')
+
+ folder = create(:folder)
+ file2 = folder.user_files.build(:attachment => fixture_file)
+ file2.attachment_file_name = 'Test.txt'
+ assert file2.save
+
+ file3 = Folder.root.user_files.build(:attachment => fixture_file)
+ file3.attachment_file_name = 'Test.txt'
+ assert !file3.save
+ end
+
+ test 'attachment file name cannot contain invalid characters' do
+ file = create(:user_file)
+
+ %w{< > : " / \ | ? *}.each do |invalid_character|
+ file.attachment_file_name = "Test#{invalid_character}.txt"
+ assert !file.save
+ end
+
+ file.attachment_file_name = 'Test.txt'
+ assert file.save
+ end
+
+ test 'cannot copy a file to anything other than a folder' do
+ file1 = create(:user_file)
+ file2 = create(:user_file)
+ folder = create(:folder)
+
+ assert_raise(ActiveRecord::RecordInvalid) { file1.copy(nil) }
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { file1.copy('A string...') }
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { file1.copy(file2) }
+ assert file1.copy(folder)
+ end
+
+ test 'copy a file' do
+ folder = create(:folder)
+ file = create(:user_file)
+
+ assert_raise(ActiveRecord::RecordInvalid) { file.copy(Folder.root) }
+ assert_equal UserFile.all.count, 1
+ assert_equal folder.user_files.count, 0
+
+ file.copy(folder)
+ assert_equal UserFile.all.count, 2
+ assert_equal folder.user_files.count, 1
+
+ new_file = UserFile.find_by_attachment_file_name_and_folder_id(file.attachment_file_name, folder.id)
+ assert File.exists?(new_file.attachment.path)
+ end
+
+ test 'cannot move a file to anything other than a folder' do
+ file1 = create(:user_file)
+ file2 = create(:user_file)
+ folder = create(:folder)
+
+ assert_raise(ActiveRecord::RecordInvalid) { file1.move(nil) }
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { file1.move('A string...') }
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { file1.move(file2) }
+ assert file1.move(folder)
+ end
+
+ test 'move a file' do
+ folder = create(:folder)
+ folder2 = create(:folder)
+ file = create(:user_file)
+
+ assert file.copy(folder)
+ assert_equal UserFile.all.count, 2
+ assert_not_equal file.folder, folder
+ assert_raise(ActiveRecord::RecordInvalid) { file.move(folder) }
+
+ assert file.move(folder2)
+ assert_equal UserFile.all.count, 2
+ assert_equal file.folder, folder2
+ end
+
+ test 'file has correct extension' do
+ file = create(:user_file)
+ assert_equal file.extension, 'txt'
+
+ file.update_attributes(:attachment_file_name => 'test.pdf')
+ assert_equal file.extension, 'pdf'
+
+ file.update_attributes(:attachment_file_name => 'test')
+ assert file.extension.blank?
+ end
+end
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
new file mode 100644
index 0000000..a121f3a
--- /dev/null
+++ b/test/unit/user_test.rb
@@ -0,0 +1,237 @@
+require 'test_helper'
+
+class UserTest < ActiveSupport::TestCase
+ test 'password is valid' do
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :password => '123456', :password_confirmation => '654321') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :password => '123') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :password => '') }
+ assert create(:user, :password => '123456')
+
+ user = create(:user)
+ user.password_required = false
+ user.password = ''
+ user.password_confirmation = ''
+ assert user.save
+ end
+
+ test 'name can be empty for a new user' do
+ create(:user, :name => '', :email => 'test@test.com')
+ assert User.exists?(:name => '', :email => 'test@test.com')
+ end
+
+ test 'signup_token and signup_token_expires_at are set for a new user' do
+ user = create(:user, :name => '', :email => 'test@test.com')
+ assert !user.signup_token.empty?
+ assert user.signup_token_expires_at > 1.hour.from_now
+ end
+
+ test 'name cannot be empty for an existing user' do
+ user = create(:user)
+ user.name = ''
+ assert_raise(ActiveRecord::RecordInvalid) { user.save! }
+ end
+
+ test 'signup_token and signup_token_expires_at are nil for an existing user' do
+ user = create(:user, :name => '', :email => 'test@test.com')
+ user.name = 'test'
+ user.save
+ assert user.signup_token.nil?
+ assert user.signup_token_expires_at.nil?
+ end
+
+ test 'email is not empty' do
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :email => '') }
+ end
+
+ test 'name is unique' do
+ create(:user, :name => 'Test')
+ assert User.exists?(:name => 'Test')
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :name => 'Test') }
+ end
+
+ test 'email is unique' do
+ create(:user, :email => 'test@test.com')
+ assert User.exists?(:email => 'test@test.com')
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :email => 'test@test.com') }
+ end
+
+ test 'email is valid' do
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :email => '@.com') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :email => '@test.com') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :email => 'test@.com') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :email => 'test@test.') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :email => 'test@$%^.com') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :email => 'test@test.c') }
+ assert_raise(ActiveRecord::RecordInvalid) { create(:user, :email => 'test@test.$$$') }
+ assert_nothing_raised(ActiveRecord::RecordInvalid) { create(:user, :email => 'test@test.com') }
+ end
+
+ test 'reset_password_token gets cleared' do
+ token = SecureRandom.base64(32)
+ user = create(:user, :reset_password_token => token, :dont_clear_reset_password_token => true)
+ assert_equal user.reset_password_token, token
+
+ user2 = User.find_by_reset_password_token(token)
+ user2.save
+ assert user2.reset_password_token.blank?
+ end
+
+ test 'root folder and admins group get created' do
+ admin = create(:user, :is_admin => true)
+ assert_equal Folder.where(:name => 'Root folder').count, 1
+ assert_equal Group.where(:name => 'Admins').count, 1
+ assert_equal admin.groups.count, 1
+ end
+
+ test 'cannot delete admin user' do
+ admin = create(:user, :is_admin => true)
+ normal_user = create(:user)
+
+ assert_raise(RuntimeError) { admin.destroy }
+ assert normal_user.destroy
+ end
+
+ test 'user permissions' do
+ admin = create(:user, :is_admin => true)
+ user = create(:user)
+ root = Folder.root
+ folder = create(:folder)
+ group = create(:group)
+
+ %w{create read update delete}.each { |method| assert admin.send("can_#{method}", root) }
+ %w{create read update delete}.each { |method| assert admin.send("can_#{method}", folder) }
+ %w{create read update delete}.each { |method| assert !user.send("can_#{method}", root) }
+ %w{create read update delete}.each { |method| assert !user.send("can_#{method}", folder) }
+
+ user.groups << group
+ assert user.can_read(root)
+ %w{create update delete}.each { |method| assert !user.send("can_#{method}", root) }
+ %w{create read update delete}.each { |method| assert !user.send("can_#{method}", folder) }
+
+ folder.permissions.where(:group_id => group).update_all(:can_create => true)
+ assert user.can_create(folder)
+ %w{read update delete}.each { |method| assert !user.send("can_#{method}", folder) }
+
+ folder.permissions.where(:group_id => group).update_all(:can_read => true)
+ %w{create read}.each { |method| assert user.send("can_#{method}", folder) }
+ %w{update delete}.each { |method| assert !user.send("can_#{method}", folder) }
+
+ folder.permissions.where(:group_id => group).update_all(:can_update => true)
+ %w{create read update}.each { |method| assert user.send("can_#{method}", folder) }
+ assert !user.can_delete(folder)
+
+ folder.permissions.where(:group_id => group).update_all(:can_delete => true)
+ %w{create read update delete}.each { |method| assert user.send("can_#{method}", folder) }
+
+ assert user.can_read(root)
+ %w{create update delete}.each { |method| assert !user.send("can_#{method}", root) }
+ %w{create read update delete}.each { |method| assert admin.send("can_#{method}", root) }
+ %w{create read update delete}.each { |method| assert admin.send("can_#{method}", folder) }
+ end
+
+ test 'hashed_password and password_salt do not change when leaving the password empty' do
+ user = create(:user)
+ assert !user.password_salt.blank?
+ assert !user.hashed_password.blank?
+
+ salt = user.password_salt
+ hash = user.hashed_password
+
+ user.password_required = false
+ user.password = ''
+ user.password_confirmation = ''
+
+ assert user.save
+ assert_equal user.password_salt, salt
+ assert_equal user.hashed_password, hash
+ end
+
+ test 'hashed_password and password_salt change when updating the password' do
+ user = create(:user)
+ salt = user.password_salt
+ hash = user.hashed_password
+
+ user.password = 'test1234'
+ user.password_confirmation = 'test1234'
+
+ assert user.save
+ assert_not_equal user.password_salt, salt
+ assert_not_equal user.hashed_password, hash
+ assert User.authenticate(user.name, 'test1234')
+ end
+
+ test 'whether a user is member of admins or not' do
+ admin = create(:user, :is_admin => true)
+ assert admin.member_of_admins?
+
+ user = create(:user)
+ assert !user.member_of_admins?
+
+ user.groups << Group.find_by_name('Admins')
+ assert user.member_of_admins?
+ end
+
+ test 'whether reset_password_token refreshes' do
+ user = create(:user)
+ assert user.reset_password_token.blank?
+
+ user.refresh_reset_password_token
+ assert !user.reset_password_token.blank?
+ assert_in_delta user.reset_password_token_expires_at, 1.hour.from_now, 1.second
+
+ token = user.reset_password_token
+ user.refresh_reset_password_token
+
+ assert !user.reset_password_token.blank?
+ assert_not_equal user.reset_password_token, token
+ end
+
+ test 'whether remember_token refreshes' do
+ user = create(:user)
+ assert user.remember_token.blank?
+
+ user.refresh_remember_token
+ assert !user.remember_token.blank?
+
+ token = user.remember_token
+ user.refresh_remember_token
+
+ assert !user.remember_token.blank?
+ assert_not_equal user.remember_token, token
+ end
+
+ test 'whether forget_me clears remember_token' do
+ user = create(:user)
+ user.refresh_remember_token
+ assert !user.remember_token.blank?
+
+ user.forget_me
+ assert user.remember_token.blank?
+ assert !user.changed? # There are no unsaved changes: `forget_me` saved the record
+ end
+
+ test 'authentication' do
+ user = create(:user, :name => 'testname', :password => 'secret')
+ assert !User.authenticate(nil, nil)
+ assert !User.authenticate('', '')
+ assert !User.authenticate('testname', nil)
+ assert !User.authenticate('testname', '')
+ assert !User.authenticate(nil, 'secret')
+ assert !User.authenticate('', 'secret')
+ assert !User.authenticate('test', 'test')
+ assert User.authenticate('testname', 'secret')
+ end
+
+ test 'whether there is an admin user or not' do
+ assert User.no_admin_yet?
+
+ normal_user = create(:user)
+ assert User.no_admin_yet?
+
+ # make normal_user admin
+ normal_user.is_admin = true
+ normal_user.save
+
+ assert !User.no_admin_yet?
+ end
+end