├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── .tx └── config ├── Cakefile ├── LICENSE ├── README.md ├── build ├── client │ ├── app │ │ └── locales │ │ │ ├── de.js │ │ │ ├── en.js │ │ │ ├── es.js │ │ │ ├── fr.js │ │ │ └── ro.js │ └── public │ │ ├── ViewerJS │ │ ├── AGPL-3.0.txt │ │ ├── ODFViewerPlugin.css │ │ ├── ODFViewerPlugin.js │ │ ├── PDFViewerPlugin.css │ │ ├── PDFViewerPlugin.js │ │ ├── PluginLoader.js │ │ ├── compatibility.js │ │ ├── example.local.css │ │ ├── images │ │ │ ├── kogmbh.png │ │ │ ├── nlnet.png │ │ │ ├── texture.png │ │ │ ├── toolbarButton-download.png │ │ │ ├── toolbarButton-fullscreen.png │ │ │ ├── toolbarButton-menuArrows.png │ │ │ ├── toolbarButton-pageDown.png │ │ │ ├── toolbarButton-pageUp.png │ │ │ ├── toolbarButton-presentation.png │ │ │ ├── toolbarButton-zoomIn.png │ │ │ └── toolbarButton-zoomOut.png │ │ ├── index.html │ │ ├── pdf.js │ │ ├── pdf.worker.js │ │ ├── pdf_find_bar.js │ │ ├── pdf_find_controller.js │ │ ├── pdfjsversion.js │ │ ├── text_layer_builder.js │ │ ├── ui_utils.js │ │ ├── viewer.css │ │ ├── viewer.js │ │ └── webodf.js │ │ ├── android-chrome-144x144.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-36x36.png │ │ ├── android-chrome-48x48.png │ │ ├── android-chrome-72x72.png │ │ ├── android-chrome-96x96.png │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-precomposed.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── crossdomain.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-194x194.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── fonts │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── fonts.css │ │ ├── mavenpro-bold.woff │ │ ├── mavenpro-bold.woff2 │ │ ├── mavenpro-regular.woff │ │ ├── mavenpro-regular.woff2 │ │ ├── sourcesanspro-bold-italic.woff │ │ ├── sourcesanspro-bold-italic.woff2 │ │ ├── sourcesanspro-bold.woff │ │ ├── sourcesanspro-bold.woff2 │ │ ├── sourcesanspro-italic.woff │ │ ├── sourcesanspro-italic.woff2 │ │ ├── sourcesanspro-regular.woff │ │ └── sourcesanspro-regular.woff2 │ │ ├── humans.txt │ │ ├── icons │ │ └── main_icon.png │ │ ├── images │ │ ├── Melamine-wood-003.png │ │ ├── Melamine-wood-004.png │ │ ├── background.jpg │ │ ├── clippy-map.png │ │ ├── cozy-logo.png │ │ ├── defaultpicture.png │ │ ├── magnifier.svg │ │ ├── spinner.svg │ │ ├── sprite-files-type.svg │ │ └── sprite-files.svg │ │ ├── javascripts │ │ ├── app-c27aea7a.js │ │ ├── app.js │ │ ├── app.js.map │ │ ├── clippy-agent.js │ │ ├── clippy-sounds-mp3.js │ │ ├── clippy.min.js │ │ ├── modernizr-2.6.1.js │ │ ├── vendor-23dae376.js │ │ ├── vendor.js │ │ └── vendor.js.map │ │ ├── manifest.json │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ ├── mstile-70x70.png │ │ └── stylesheets │ │ ├── app-6f40a950.css │ │ ├── app.css │ │ ├── app.css.map │ │ └── clippy.css ├── server.js └── server │ ├── config.js │ ├── controllers │ ├── files.js │ ├── folders.js │ ├── index.js │ ├── routes.js │ └── sharing.js │ ├── helpers │ ├── file.js │ ├── get_template_ext.js │ ├── init.js │ ├── path.js │ ├── sharing.js │ └── update_parents.js │ ├── initialize.js │ ├── lib │ ├── feed.js │ └── localization_manager.js │ ├── locales │ ├── en.js │ └── fr.js │ ├── middlewares │ ├── errors.js │ └── public_auth.js │ ├── models │ ├── binary.js │ ├── contact.js │ ├── file.js │ ├── folder.js │ ├── requests.js │ └── user.js │ └── views │ ├── 404.js │ ├── en │ ├── notifmail.js │ └── sharemail.js │ ├── fr │ ├── notifmail.js │ └── sharemail.js │ └── index.js ├── client ├── .gitignore ├── app │ ├── application.coffee │ ├── assets │ │ ├── ViewerJS │ │ │ ├── AGPL-3.0.txt │ │ │ ├── ODFViewerPlugin.css │ │ │ ├── ODFViewerPlugin.js │ │ │ ├── PDFViewerPlugin.css │ │ │ ├── PDFViewerPlugin.js │ │ │ ├── PluginLoader.js │ │ │ ├── compatibility.js │ │ │ ├── example.local.css │ │ │ ├── images │ │ │ │ ├── kogmbh.png │ │ │ │ ├── nlnet.png │ │ │ │ ├── texture.png │ │ │ │ ├── toolbarButton-download.png │ │ │ │ ├── toolbarButton-fullscreen.png │ │ │ │ ├── toolbarButton-menuArrows.png │ │ │ │ ├── toolbarButton-pageDown.png │ │ │ │ ├── toolbarButton-pageUp.png │ │ │ │ ├── toolbarButton-presentation.png │ │ │ │ ├── toolbarButton-zoomIn.png │ │ │ │ └── toolbarButton-zoomOut.png │ │ │ ├── index.html │ │ │ ├── pdf.js │ │ │ ├── pdf.worker.js │ │ │ ├── pdf_find_bar.js │ │ │ ├── pdf_find_controller.js │ │ │ ├── pdfjsversion.js │ │ │ ├── text_layer_builder.js │ │ │ ├── ui_utils.js │ │ │ ├── viewer.css │ │ │ ├── viewer.js │ │ │ └── webodf.js │ │ ├── android-chrome-144x144.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-36x36.png │ │ ├── android-chrome-48x48.png │ │ ├── android-chrome-72x72.png │ │ ├── android-chrome-96x96.png │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-precomposed.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── crossdomain.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-194x194.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── fonts.css │ │ │ ├── mavenpro-bold.woff │ │ │ ├── mavenpro-bold.woff2 │ │ │ ├── mavenpro-regular.woff │ │ │ ├── mavenpro-regular.woff2 │ │ │ ├── sourcesanspro-bold-italic.woff │ │ │ ├── sourcesanspro-bold-italic.woff2 │ │ │ ├── sourcesanspro-bold.woff │ │ │ ├── sourcesanspro-bold.woff2 │ │ │ ├── sourcesanspro-italic.woff │ │ │ ├── sourcesanspro-italic.woff2 │ │ │ ├── sourcesanspro-regular.woff │ │ │ └── sourcesanspro-regular.woff2 │ │ ├── humans.txt │ │ ├── icons │ │ │ └── main_icon.png │ │ ├── images │ │ │ ├── .gitkeep │ │ │ ├── Melamine-wood-003.png │ │ │ ├── Melamine-wood-004.png │ │ │ ├── background.jpg │ │ │ ├── clippy-map.png │ │ │ ├── cozy-logo.png │ │ │ ├── defaultpicture.png │ │ │ ├── magnifier.svg │ │ │ ├── spinner.svg │ │ │ ├── sprite-files-type.svg │ │ │ └── sprite-files.svg │ │ ├── javascripts │ │ │ ├── clippy-agent.js │ │ │ ├── clippy-sounds-mp3.js │ │ │ ├── clippy.min.js │ │ │ └── modernizr-2.6.1.js │ │ ├── manifest.json │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ ├── mstile-70x70.png │ │ └── stylesheets │ │ │ └── clippy.css │ ├── collections │ │ ├── .gitkeep │ │ ├── files.coffee │ │ └── upload_queue.coffee │ ├── initialize.coffee │ ├── lib │ │ ├── base_view.coffee │ │ ├── client.coffee │ │ ├── folder_helpers.coffee │ │ ├── long-list-rows.coffee │ │ ├── socket.coffee │ │ └── view_collection.coffee │ ├── locales │ │ ├── de.json │ │ ├── en.json │ │ ├── es.json │ │ ├── fr.json │ │ └── ro.json │ ├── models │ │ ├── .gitkeep │ │ └── file.coffee │ ├── router.coffee │ ├── utils │ │ └── plugin_utils.coffee │ ├── views │ │ ├── breadcrumbs.coffee │ │ ├── file_info.coffee │ │ ├── file_view.coffee │ │ ├── files.coffee │ │ ├── folder.coffee │ │ ├── gallery.coffee │ │ ├── modal.coffee │ │ ├── modal_bulk_move.coffee │ │ ├── modal_conflict.coffee │ │ ├── modal_share.coffee │ │ ├── public_folder.coffee │ │ ├── styles │ │ │ ├── _colors.styl │ │ │ ├── _cozy.styl │ │ │ ├── _icons.styl │ │ │ ├── _list.styl │ │ │ ├── _main.styl │ │ │ ├── _responsive.styl │ │ │ ├── _states.styl │ │ │ └── application.styl │ │ ├── templates │ │ │ ├── breadcrumbs_element.jade │ │ │ ├── file.jade │ │ │ ├── file_edit.jade │ │ │ ├── files.jade │ │ │ ├── folder.jade │ │ │ ├── modal.jade │ │ │ ├── modal_folder.jade │ │ │ ├── progressbar.jade │ │ │ └── upload_status.jade │ │ └── upload_status.coffee │ └── widgets │ │ ├── progressbar.coffee │ │ └── tags.coffee ├── config.coffee ├── generators │ ├── collection │ │ ├── collection.coffee.hbs │ │ └── generator.json │ ├── collection_test │ │ ├── collection_test.coffee.hbs │ │ └── generator.json │ ├── generator │ │ ├── generated_file.coffee.hbs │ │ ├── generator.json │ │ └── generator.json.hbs │ ├── model │ │ ├── generator.json │ │ └── model.coffee.hbs │ ├── model_test │ │ ├── generator.json │ │ └── model_test.coffee.hbs │ ├── module │ │ ├── collection.coffee.hbs │ │ ├── generator.json │ │ ├── model.coffee.hbs │ │ ├── style.styl.hbs │ │ ├── template.jade.hbs │ │ ├── view.coffee.hbs │ │ └── view_collection.coffee.hbs │ ├── style │ │ ├── generator.json │ │ └── style.styl.hbs │ ├── template │ │ ├── generator.json │ │ └── template.hbs │ ├── view │ │ ├── generator.json │ │ └── view.coffee.hbs │ └── view_test │ │ ├── generator.json │ │ └── view_test.coffee.hbs ├── package.json ├── tests │ ├── e2e │ │ ├── bulk_actions.coffee │ │ ├── share.coffee │ │ └── upload.coffee │ └── helpers.coffee └── vendor │ ├── plugins │ ├── gallery │ │ └── init.js │ ├── markdown │ │ ├── init.js │ │ └── markdown.js │ ├── medias │ │ └── init.js │ └── viewer │ │ └── init.js │ ├── scripts │ ├── async.js │ ├── backbone-1.1.2.js │ ├── backbone.projections.js │ ├── baguetteBox.js │ ├── bootstrap.js │ ├── bootstrap.typeahead.js │ ├── clearance_modal.js │ ├── cozy-realtime.js │ ├── filesize.min.js │ ├── finite-state-machine.js │ ├── jade-runtime.js │ ├── jquery-1.9.1.js │ ├── jquery-ui-1.10.3.custom.js │ ├── jquery.spin.js │ ├── moment-with-langs.js │ ├── polyglot.js │ ├── socket.io.js │ ├── spin.js │ ├── tag-it.js │ ├── typeahead.bundle.js │ └── underscore-1.4.4.js │ └── styles │ ├── baguetteBox.css │ ├── bootstrap-theme.min.css │ ├── bootstrap.min.css │ ├── gradient.css │ ├── helpers.css │ ├── jquery.tagit.css │ ├── normalize.css │ └── tagit.ui-zendesk.css ├── coffeelint.json ├── package.json ├── server.coffee ├── server ├── config.coffee ├── controllers │ ├── files.coffee │ ├── folders.coffee │ ├── index.coffee │ ├── routes.coffee │ └── sharing.coffee ├── helpers │ ├── file.coffee │ ├── get_template_ext.coffee │ ├── init.coffee │ ├── path.coffee │ ├── sharing.coffee │ └── update_parents.coffee ├── initialize.coffee ├── lib │ ├── feed.coffee │ └── localization_manager.coffee ├── locales │ ├── en.json │ └── fr.json ├── middlewares │ ├── errors.coffee │ └── public_auth.coffee ├── models │ ├── binary.coffee │ ├── contact.coffee │ ├── file.coffee │ ├── folder.coffee │ ├── requests.coffee │ └── user.coffee └── views │ ├── 404.jade │ ├── 404_build.jade │ ├── en │ ├── notifmail.jade │ └── sharemail.jade │ ├── fr │ ├── notifmail.jade │ └── sharemail.jade │ ├── index.jade │ └── index_build.jade └── test ├── create_batch_of_directories.coffee ├── create_batch_of_files.coffee ├── create_problematic_directory.sh ├── create_problematic_file.sh ├── files_test.coffee ├── fixtures ├── files.json ├── files │ ├── chat-mignon1.jpg │ ├── chat-mignon2.jpg │ ├── chaton-innocent.jpg │ ├── test.txt │ └── test2.txt ├── files_to_remove.json └── folders.json ├── folders_test.coffee ├── helpers.coffee └── sharing_test.coffee /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | # editorconfig is a unified configuration file that all editors can take 3 | # into account 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 4 13 | 14 | [*.jade] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /client/node_modules/ 3 | /client/public/ 4 | .DS_Store 5 | 6 | *.swp 7 | *.log 8 | *.transifex.coffee 9 | 10 | /test/fixtures/big-folder/ 11 | /test/fixtures/big-folder-of-folders/ 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | lib-cov 3 | *.seed 4 | *.csv 5 | *.log 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | client 12 | !build/client 13 | server 14 | !build/server 15 | test 16 | server.coffee 17 | .editorconfig 18 | .tx 19 | .travis.yml 20 | Cakefile 21 | coffeelint.json 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | matrix: 4 | fast_finish: true 5 | allow_failures: 6 | - node_js: 6 7 | node_js: 8 | - 0.10 9 | - 0.12 10 | - 4 11 | - 6 12 | services: 13 | - couchdb 14 | env: 15 | global: 16 | - NODE_ENV=test 17 | - CXX=g++-4.8 18 | addons: 19 | apt: 20 | sources: 21 | - ubuntu-toolchain-r-test 22 | packages: 23 | - gcc-4.8 24 | - g++-4.8 25 | 26 | before_install: 27 | - travis_retry git clone git://github.com/cozy/cozy-data-system.git 28 | - cd cozy-data-system 29 | - travis_retry npm install forever coffee-script -g 30 | - travis_retry npm install #data-system 31 | - pwd 32 | - NAME=data-system TOKEN=token forever start -o forever-ds.log build/server.js 33 | - ps aux | grep server.js 34 | - sleep 5 35 | - cat forever-ds.log 36 | - curl http://localhost:9101/ 37 | - coffee commands.coffee test-install files 38 | - cd .. 39 | - export NAME=files 40 | - export TOKEN=apptoken 41 | - mkdir log 42 | 43 | after_failure: 44 | - cat cozy-data-system/forever-ds.log 45 | - cat cozy-data-system/log/test.log 46 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [cozy-files.enjson] 5 | file_filter = client/app/locales/.json 6 | source_file = client/app/locales/en.json 7 | source_lang = en 8 | type = KEYVALUEJSON 9 | 10 | [cozy-files.server-enjson] 11 | file_filter = server/locales/.json 12 | source_file = server/locales/en.json 13 | source_lang = en 14 | type = KEYVALUEJSON 15 | -------------------------------------------------------------------------------- /build/client/public/ViewerJS/ODFViewerPlugin.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013-2014 KO GmbH 3 | * 4 | * @licstart 5 | * This file is part of WebODF. 6 | * 7 | * WebODF is free software: you can redistribute it and/or modify it 8 | * under the terms of the GNU Affero General Public License (GNU AGPL) 9 | * as published by the Free Software Foundation, either version 3 of 10 | * the License, or (at your option) any later version. 11 | * 12 | * WebODF is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with WebODF. If not, see . 19 | * @licend 20 | * 21 | * @source: http://www.webodf.org/ 22 | * @source: https://github.com/kogmbh/WebODF/ 23 | */ 24 | 25 | @namespace cursor url(urn:webodf:names:cursor); 26 | 27 | .caret { 28 | opacity: 0 !important; 29 | } 30 | -------------------------------------------------------------------------------- /build/client/public/ViewerJS/PDFViewerPlugin.css: -------------------------------------------------------------------------------- 1 | .page { 2 | margin: 7px auto 7px auto; 3 | position: relative; 4 | overflow: hidden; 5 | background-clip: content-box; 6 | background-color: white; 7 | 8 | box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 9 | -webkit-box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 10 | -moz-box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 11 | -ms-box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 12 | -o-box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 13 | } 14 | 15 | .textLayer { 16 | position: absolute; 17 | left: 0; 18 | top: 0; 19 | right: 0; 20 | bottom: 0; 21 | color: #000; 22 | font-family: sans-serif; 23 | overflow: hidden; 24 | } 25 | 26 | .textLayer > div { 27 | color: transparent; 28 | position: absolute; 29 | line-height: 1; 30 | white-space: pre; 31 | cursor: text; 32 | } 33 | 34 | ::selection { background:rgba(0,0,255,0.3); } 35 | ::-moz-selection { background:rgba(0,0,255,0.3); } 36 | 37 | -------------------------------------------------------------------------------- /build/client/public/ViewerJS/example.local.css: -------------------------------------------------------------------------------- 1 | /* This is just a sample file with CSS rules. You should write your own @font-face declarations 2 | * to add support for your desired fonts. 3 | */ 4 | 5 | @font-face { 6 | font-family: 'Novecentowide Book'; 7 | src: url("/ViewerJS/fonts/Novecentowide-Bold-webfont.eot"); 8 | src: url("/ViewerJS/fonts/Novecentowide-Bold-webfont.eot?#iefix") format("embedded-opentype"), 9 | url("/ViewerJS/fonts/Novecentowide-Bold-webfont.woff") format("woff"), 10 | url("/fonts/Novecentowide-Bold-webfont.ttf") format("truetype"), 11 | url("/fonts/Novecentowide-Bold-webfont.svg#NovecentowideBookBold") format("svg"); 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | 16 | @font-face { 17 | font-family: 'exotica'; 18 | src: url('/ViewerJS/fonts/Exotica-webfont.eot'); 19 | src: url('/ViewerJS/fonts/Exotica-webfont.eot?#iefix') format('embedded-opentype'), 20 | url('/ViewerJS/fonts/Exotica-webfont.woff') format('woff'), 21 | url('/ViewerJS/fonts/Exotica-webfont.ttf') format('truetype'), 22 | url('/ViewerJS/fonts/Exotica-webfont.svg#exoticamedium') format('svg'); 23 | font-weight: normal; 24 | font-style: normal; 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/kogmbh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/kogmbh.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/nlnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/nlnet.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/texture.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/toolbarButton-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/toolbarButton-download.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/toolbarButton-fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/toolbarButton-fullscreen.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/toolbarButton-menuArrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/toolbarButton-menuArrows.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/toolbarButton-pageDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/toolbarButton-pageDown.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/toolbarButton-pageUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/toolbarButton-pageUp.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/toolbarButton-presentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/toolbarButton-presentation.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/toolbarButton-zoomIn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/toolbarButton-zoomIn.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/images/toolbarButton-zoomOut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/ViewerJS/images/toolbarButton-zoomOut.png -------------------------------------------------------------------------------- /build/client/public/ViewerJS/pdfjsversion.js: -------------------------------------------------------------------------------- 1 | var /**@const{!string}*/pdfjs_version = "d45d7bc"; 2 | -------------------------------------------------------------------------------- /build/client/public/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/android-chrome-144x144.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/android-chrome-36x36.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/android-chrome-48x48.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/android-chrome-72x72.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/android-chrome-96x96.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/apple-touch-icon.png -------------------------------------------------------------------------------- /build/client/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #8cb1ff 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /build/client/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /build/client/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/favicon-16x16.png -------------------------------------------------------------------------------- /build/client/public/favicon-194x194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/favicon-194x194.png -------------------------------------------------------------------------------- /build/client/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/favicon-32x32.png -------------------------------------------------------------------------------- /build/client/public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/favicon-96x96.png -------------------------------------------------------------------------------- /build/client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/favicon.ico -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/mavenpro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/mavenpro-bold.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/mavenpro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/mavenpro-regular.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/sourcesanspro-bold-italic.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/sourcesanspro-bold-italic.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/sourcesanspro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/sourcesanspro-bold.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/sourcesanspro-italic.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/sourcesanspro-italic.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/sourcesanspro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/fonts/sourcesanspro-regular.woff2 -------------------------------------------------------------------------------- /build/client/public/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | jQuery, Modernizr, 16 | Lodash, Backbone, 17 | Handlebars, Coffeescript, 18 | Stylus, Brunch 19 | -------------------------------------------------------------------------------- /build/client/public/icons/main_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/icons/main_icon.png -------------------------------------------------------------------------------- /build/client/public/images/Melamine-wood-003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/images/Melamine-wood-003.png -------------------------------------------------------------------------------- /build/client/public/images/Melamine-wood-004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/images/Melamine-wood-004.png -------------------------------------------------------------------------------- /build/client/public/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/images/background.jpg -------------------------------------------------------------------------------- /build/client/public/images/clippy-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/images/clippy-map.png -------------------------------------------------------------------------------- /build/client/public/images/cozy-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/images/cozy-logo.png -------------------------------------------------------------------------------- /build/client/public/images/defaultpicture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/images/defaultpicture.png -------------------------------------------------------------------------------- /build/client/public/images/magnifier.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | magnifier 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /build/client/public/images/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/client/public/images/sprite-files.svg: -------------------------------------------------------------------------------- 1 | sprite-filesCreated with Sketch. -------------------------------------------------------------------------------- /build/client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cozy Files", 3 | "icons": [ 4 | { 5 | "src": "\/apps\/files\/android-chrome-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/apps\/files\/android-chrome-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/apps\/files\/android-chrome-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/apps\/files\/android-chrome-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/apps\/files\/android-chrome-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/apps\/files\/android-chrome-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /build/client/public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/mstile-144x144.png -------------------------------------------------------------------------------- /build/client/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/mstile-150x150.png -------------------------------------------------------------------------------- /build/client/public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/mstile-310x150.png -------------------------------------------------------------------------------- /build/client/public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/mstile-310x310.png -------------------------------------------------------------------------------- /build/client/public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/build/client/public/mstile-70x70.png -------------------------------------------------------------------------------- /build/client/public/stylesheets/clippy.css: -------------------------------------------------------------------------------- 1 | .clippy, .clippy-balloon { 2 | position: fixed; 3 | z-index: 1000; 4 | cursor: pointer; 5 | } 6 | 7 | .clippy-balloon { 8 | 9 | background: #FFC; 10 | color: black; 11 | padding: 8px; 12 | border: 1px solid black; 13 | border-radius: 5px; 14 | 15 | } 16 | 17 | .clippy-content { 18 | max-width: 200px; 19 | min-width: 120px; 20 | font-family: "Microsoft Sans", sans-serif; 21 | font-size: 10pt; 22 | } 23 | 24 | .clippy-tip { 25 | width: 10px; 26 | height: 16px; 27 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAgCAMAAAAlvKiEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRF///MAAAA////52QwgAAAAAN0Uk5T//8A18oNQQAAAGxJREFUeNqs0kEOwCAIRFHn3//QTUU6xMyyxii+jQosrTPkyPEM6IN3FtzIRk1U4dFeKWQiH6pRRowMVKEmvronEynkwj0uZJgR22+YLopPSo9P34wJSamLSU7lSIWLJU7NkNomNlhqxUeAAQC+TQLZyEuJBwAAAABJRU5ErkJggg==) no-repeat; 28 | position: absolute; 29 | } 30 | 31 | .clippy-top-left .clippy-tip { 32 | top: 100%; 33 | margin-top: 0px; 34 | left: 100%; 35 | margin-left: -50px; 36 | } 37 | 38 | .clippy-top-right .clippy-tip { 39 | top: 100%; 40 | margin-top: 0px; 41 | left: 0; 42 | margin-left: 50px; 43 | background-position: -10px 0; 44 | 45 | } 46 | 47 | .clippy-bottom-right .clippy-tip { 48 | top: 0; 49 | margin-top: -16px; 50 | left: 0; 51 | margin-left: 50px; 52 | background-position: -10px -16px; 53 | } 54 | 55 | .clippy-bottom-left .clippy-tip { 56 | top: 0; 57 | margin-top: -16px; 58 | left: 100%; 59 | margin-left: -50px; 60 | background-position: 0px -16px; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /build/server.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var americano, application, errorHandler, initialize; 3 | 4 | americano = require('americano'); 5 | 6 | errorHandler = require('./server/middlewares/errors'); 7 | 8 | initialize = require('./server/initialize'); 9 | 10 | application = module.exports = function(callback) { 11 | var options; 12 | options = { 13 | name: 'cozy-files', 14 | root: __dirname, 15 | port: process.env.PORT || 9121, 16 | host: process.env.HOST || '127.0.0.1' 17 | }; 18 | return initialize.beforeStart(function() { 19 | return americano.start(options, function(err, app, server) { 20 | app.server = server; 21 | app.use(errorHandler); 22 | return initialize.afterStart(app, server, callback); 23 | }); 24 | }); 25 | }; 26 | 27 | if (!module.parent) { 28 | application(); 29 | } 30 | -------------------------------------------------------------------------------- /build/server/config.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var GB, americano, config, errorHandler, getTemplateExt, path, publicStatic, staticMiddleware; 3 | 4 | path = require('path'); 5 | 6 | americano = require('americano'); 7 | 8 | errorHandler = require('./middlewares/errors'); 9 | 10 | getTemplateExt = require('./helpers/get_template_ext'); 11 | 12 | staticMiddleware = americano["static"](path.resolve(__dirname, '../client/public'), { 13 | maxAge: 86400000 14 | }); 15 | 16 | publicStatic = function(req, res, next) { 17 | var assetsMatched, detectAssets; 18 | detectAssets = /\/(stylesheets|javascripts|images|fonts)+\/(.+)$/; 19 | assetsMatched = detectAssets.exec(req.url); 20 | if (assetsMatched != null) { 21 | req.url = assetsMatched[0]; 22 | } 23 | return staticMiddleware(req, res, function(err) { 24 | return next(err); 25 | }); 26 | }; 27 | 28 | GB = 1024 * 1024 * 1024; 29 | 30 | config = { 31 | common: { 32 | set: { 33 | 'view engine': getTemplateExt(), 34 | 'views': path.resolve(__dirname, 'views') 35 | }, 36 | engine: { 37 | js: function(path, locales, callback) { 38 | return callback(null, require(path)(locales)); 39 | } 40 | }, 41 | use: [americano.bodyParser(), staticMiddleware, publicStatic], 42 | afterStart: function(app, server) { 43 | return app.use(errorHandler); 44 | } 45 | }, 46 | development: [americano.logger('dev')], 47 | production: [americano.logger('short')], 48 | plugins: ['cozydb'] 49 | }; 50 | 51 | module.exports = config; 52 | -------------------------------------------------------------------------------- /build/server/controllers/index.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var async, cozydb; 3 | 4 | async = require('async'); 5 | 6 | cozydb = require('cozydb'); 7 | 8 | module.exports.main = function(req, res, next) { 9 | return async.parallel([ 10 | function(cb) { 11 | return cozydb.api.getCozyLocale(cb); 12 | }, function(cb) { 13 | return cozydb.api.getCozyTags(cb); 14 | }, function(cb) { 15 | return cozydb.api.getCozyInstance(cb); 16 | } 17 | ], function(err, results) { 18 | var domain, instance, locale, tags; 19 | if (err) { 20 | return next(err); 21 | } else { 22 | locale = results[0], tags = results[1], instance = results[2]; 23 | if (((instance != null ? instance.domain : void 0) != null) && instance.domain !== 'domain.not.set') { 24 | domain = instance.domain; 25 | if (domain.indexOf('https') === -1) { 26 | domain = "https://" + domain; 27 | } 28 | if (domain.slice('-1') === "/") { 29 | domain = domain.substring(0, domain.length - 1); 30 | } 31 | domain = domain + "/public/files/"; 32 | } else { 33 | domain = false; 34 | } 35 | return res.render("index", { 36 | imports: "window.locale = \"" + locale + "\";\nwindow.tags = \"" + (tags.join(',').replace('\"', '')) + "\".split(',');\nwindow.domain = \"" + domain + "\";" 37 | }); 38 | } 39 | }); 40 | }; 41 | -------------------------------------------------------------------------------- /build/server/helpers/file.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | module.exports = { 3 | normalizePath: function(path) { 4 | if (path === "/") { 5 | path = ""; 6 | } else if (path.length > 0 && path[0] !== '/') { 7 | path = "/" + path; 8 | } 9 | return path; 10 | }, 11 | getFileClass: function(file) { 12 | var fileClass, type; 13 | type = file.headers['content-type']; 14 | switch (type.split('/')[0]) { 15 | case 'image': 16 | fileClass = "image"; 17 | break; 18 | case 'application': 19 | fileClass = "document"; 20 | break; 21 | case 'text': 22 | fileClass = "document"; 23 | break; 24 | case 'audio': 25 | fileClass = "music"; 26 | break; 27 | case 'video': 28 | fileClass = "video"; 29 | break; 30 | default: 31 | fileClass = "file"; 32 | } 33 | return fileClass; 34 | }, 35 | folderContentComparatorFactory: function(criterion, order) { 36 | var CRITERION, ORDER; 37 | CRITERION = criterion || 'name'; 38 | ORDER = order || 'asc'; 39 | return function(f1, f2) { 40 | var e1, e2, n1, n2, sort, t1, t2; 41 | t1 = f1.docType.toLowerCase(); 42 | t2 = f2.docType.toLowerCase(); 43 | if (t1 === t2) { 44 | if (CRITERION === 'name') { 45 | n1 = f1.name.toLocaleLowerCase(); 46 | n2 = f2.name.toLocaleLowerCase(); 47 | } else if (CRITERION === "lastModification") { 48 | n1 = new Date(f1.lastModification).getTime(); 49 | n2 = new Date(f2.lastModification).getTime(); 50 | } else { 51 | n1 = f1[CRITERION]; 52 | n2 = f2[CRITERION]; 53 | } 54 | sort = ORDER === 'asc' ? -1 : 1; 55 | if (CRITERION === 'class' && n1 === n2) { 56 | n1 = f1.name.toLocaleLowerCase(); 57 | n2 = f2.name.toLocaleLowerCase(); 58 | e1 = n1.split('.').pop(); 59 | e2 = n2.split('.').pop(); 60 | if (e1 !== e2) { 61 | if (e1 > e2) { 62 | return -sort; 63 | } 64 | if (e1 < e2) { 65 | return sort; 66 | } 67 | return 0; 68 | } 69 | } 70 | if (n1 > n2) { 71 | return -sort; 72 | } else if (n1 < n2) { 73 | return sort; 74 | } else { 75 | return 0; 76 | } 77 | } else if (t1 === 'file' && t2 === 'folder') { 78 | return 1; 79 | } else { 80 | return -1; 81 | } 82 | }; 83 | } 84 | }; 85 | -------------------------------------------------------------------------------- /build/server/helpers/get_template_ext.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var fs, path; 3 | 4 | fs = require('fs'); 5 | 6 | path = require('path'); 7 | 8 | module.exports = function() { 9 | var ext, filePath; 10 | filePath = path.resolve(__dirname, "../views/index.js"); 11 | if (fs.existsSync(filePath)) { 12 | ext = 'js'; 13 | } else { 14 | ext = 'jade'; 15 | } 16 | return ext; 17 | }; 18 | -------------------------------------------------------------------------------- /build/server/helpers/init.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var File, Folder, async; 3 | 4 | File = require('../models/file'); 5 | 6 | Folder = require('../models/folder'); 7 | 8 | async = require('async'); 9 | 10 | module.exports.updateIndex = function(callback) { 11 | return File.all(function(err, files) { 12 | if (err) { 13 | return console.log(err); 14 | } else { 15 | return async.eachSeries(files, function(file) { 16 | return file.index(['name'], function() {}); 17 | }, function() { 18 | return Folder.all(function(err, folders) { 19 | if (err) { 20 | return console.log(err); 21 | } else { 22 | return async.eachSeries(folders, function(folder) { 23 | return folder.index(['name'], function() {}); 24 | }, function() { 25 | console.log('Re-indexation is done.'); 26 | if (callback) { 27 | return callback(); 28 | } 29 | }); 30 | } 31 | }); 32 | }); 33 | } 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /build/server/helpers/path.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | module.exports.checkIfPathAvailable = function(fileInfo, files, exceptionId) { 3 | var fileDoc, fileFullPath, fullPath, i, len; 4 | fullPath = fileInfo.path + "/" + fileInfo.name; 5 | for (i = 0, len = files.length; i < len; i++) { 6 | fileDoc = files[i]; 7 | fileFullPath = fileDoc.path + "/" + fileDoc.name; 8 | if ((fullPath === fileFullPath) && (fileDoc.id !== exceptionId)) { 9 | return false; 10 | } 11 | } 12 | return true; 13 | }; 14 | -------------------------------------------------------------------------------- /build/server/helpers/update_parents.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var async, log, parentFolders, timeout; 3 | 4 | async = require('async'); 5 | 6 | log = require('printit')({ 7 | prefix: 'updateParents' 8 | }); 9 | 10 | parentFolders = {}; 11 | 12 | timeout = null; 13 | 14 | module.exports.add = function(folder, lastModification) { 15 | var fullpath; 16 | folder.lastModification = lastModification; 17 | fullpath = folder.path + "/" + folder.name; 18 | parentFolders[fullpath] = { 19 | folder: folder, 20 | lastModification: lastModification 21 | }; 22 | return module.exports.resetTimeout(); 23 | }; 24 | 25 | module.exports.resetTimeout = function() { 26 | if (timeout != null) { 27 | clearTimeout(timeout); 28 | } 29 | return timeout = setTimeout(module.exports.flush, 60 * 1000); 30 | }; 31 | 32 | module.exports.flush = function(callback) { 33 | var folders; 34 | folders = parentFolders; 35 | parentFolders = {}; 36 | return async.forEachOfSeries(folders, function(entry, fullpath, done) { 37 | var data; 38 | data = { 39 | lastModification: entry.lastModification 40 | }; 41 | return entry.folder.updateAttributes(data, function(err) { 42 | if (err != null) { 43 | log.error(err); 44 | } 45 | return done(); 46 | }); 47 | }, callback); 48 | }; 49 | -------------------------------------------------------------------------------- /build/server/initialize.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var File, Folder, RealtimeAdapter, feed, init, localization; 3 | 4 | localization = require('./lib/localization_manager'); 5 | 6 | RealtimeAdapter = require('cozy-realtime-adapter'); 7 | 8 | File = require('./models/file'); 9 | 10 | Folder = require('./models/folder'); 11 | 12 | feed = require('./lib/feed'); 13 | 14 | init = require('./helpers/init'); 15 | 16 | module.exports.beforeStart = function(callback) { 17 | return localization.initialize(callback); 18 | }; 19 | 20 | module.exports.afterStart = function(app, server, callback) { 21 | var realtime, updateIndex; 22 | feed.initialize(server); 23 | realtime = RealtimeAdapter(server, ['file.*', 'folder.*', 'contact.*'], { 24 | path: '/public/socket.io' 25 | }); 26 | init.updateIndex(); 27 | updateIndex = function(type, id) { 28 | return type.find(id, function(err, file) { 29 | if (err) { 30 | if (err) { 31 | return console.log("updateIndex err", err.stack); 32 | } 33 | } 34 | if (!file) { 35 | return console.log("updateIndex : no file", id); 36 | } 37 | return file.index(['name'], function(err) {}); 38 | }); 39 | }; 40 | realtime.on('file.create', function(event, id) { 41 | return updateIndex(File, id); 42 | }); 43 | realtime.on('folder.create', function(event, id) { 44 | return updateIndex(Folder, id); 45 | }); 46 | realtime.on('file.update', function(event, id) { 47 | return updateIndex(File, id); 48 | }); 49 | realtime.on('folder.update', function(event, id) { 50 | return updateIndex(Folder, id); 51 | }); 52 | if (callback != null) { 53 | return callback(app, server); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /build/server/lib/feed.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Feed, 3 | bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; 4 | 5 | module.exports = Feed = (function() { 6 | Feed.prototype.axonSock = void 0; 7 | 8 | function Feed() { 9 | this.publish = bind(this.publish, this); 10 | this.logger = require('printit')({ 11 | date: true, 12 | prefix: 'helper/db_feed' 13 | }); 14 | } 15 | 16 | Feed.prototype.initialize = function(server) { 17 | this.startPublishingToAxon(); 18 | return server.on('close', (function(_this) { 19 | return function() { 20 | if (_this.axonSock != null) { 21 | return _this.axonSock.close(); 22 | } 23 | }; 24 | })(this)); 25 | }; 26 | 27 | Feed.prototype.startPublishingToAxon = function() { 28 | var axon, axonPort; 29 | axon = require('axon'); 30 | this.axonSock = axon.socket('pub'); 31 | axonPort = parseInt(process.env.AXON_PORT || 9105); 32 | this.axonSock.connect(axonPort); 33 | return this.logger.info('Pub server started'); 34 | }; 35 | 36 | Feed.prototype.publish = function(event, id) { 37 | this.logger.info("Publishing " + event + " " + id); 38 | if (this.axonSock != null) { 39 | return this.axonSock.send(event, id); 40 | } 41 | }; 42 | 43 | return Feed; 44 | 45 | })(); 46 | 47 | module.exports = new Feed(); 48 | -------------------------------------------------------------------------------- /build/server/lib/localization_manager.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var LocalizationManager, Polyglot, cozydb, ext, fs, getTemplateExt, jade; 3 | 4 | jade = require('jade'); 5 | 6 | fs = require('fs'); 7 | 8 | cozydb = require('cozydb'); 9 | 10 | Polyglot = require('node-polyglot'); 11 | 12 | getTemplateExt = require('../helpers/get_template_ext'); 13 | 14 | ext = getTemplateExt(); 15 | 16 | LocalizationManager = (function() { 17 | function LocalizationManager() {} 18 | 19 | LocalizationManager.prototype.polyglot = null; 20 | 21 | LocalizationManager.prototype.initialize = function(callback) { 22 | if (callback == null) { 23 | callback = function() {}; 24 | } 25 | return this.retrieveLocale((function(_this) { 26 | return function(err, locale) { 27 | if (err != null) { 28 | _this.polyglot = _this.getPolyglotByLocale(null); 29 | } else { 30 | _this.polyglot = _this.getPolyglotByLocale(locale); 31 | } 32 | return callback(null, _this.polyglot); 33 | }; 34 | })(this)); 35 | }; 36 | 37 | LocalizationManager.prototype.retrieveLocale = function(callback) { 38 | return cozydb.api.getCozyLocale(function(err, locale) { 39 | if ((err != null) || !locale) { 40 | locale = 'en'; 41 | } 42 | return callback(err, locale); 43 | }); 44 | }; 45 | 46 | LocalizationManager.prototype.getPolyglotByLocale = function(locale) { 47 | var err, error, phrases; 48 | if (locale != null) { 49 | try { 50 | phrases = require("../locales/" + locale); 51 | } catch (error) { 52 | err = error; 53 | phrases = require('../locales/en'); 54 | } 55 | } else { 56 | phrases = require('../locales/en'); 57 | } 58 | return new Polyglot({ 59 | locale: locale, 60 | phrases: phrases 61 | }); 62 | }; 63 | 64 | LocalizationManager.prototype.t = function(key, params) { 65 | var ref; 66 | if (params == null) { 67 | params = {}; 68 | } 69 | return (ref = this.polyglot) != null ? ref.t(key, params) : void 0; 70 | }; 71 | 72 | LocalizationManager.prototype.getEmailTemplate = function(name) { 73 | var getPath, templatePath; 74 | getPath = function(lang) { 75 | var filePath, templatefile; 76 | filePath = "../views/" + lang + "/" + name; 77 | templatefile = require('path').join(__dirname, filePath); 78 | if (ext !== 'jade') { 79 | templatefile = templatefile.replace('jade', 'js'); 80 | } 81 | if (fs.existsSync(templatefile)) { 82 | return templatefile; 83 | } else { 84 | return null; 85 | } 86 | }; 87 | templatePath = getPath(this.polyglot.currentLocale); 88 | if (templatePath == null) { 89 | templatePath = getPath('en'); 90 | } 91 | if (ext === 'jade') { 92 | return jade.compile(fs.readFileSync(templatePath, 'utf8')); 93 | } else { 94 | return require(templatePath); 95 | } 96 | }; 97 | 98 | LocalizationManager.prototype.getPolyglot = function() { 99 | return this.polyglot; 100 | }; 101 | 102 | return LocalizationManager; 103 | 104 | })(); 105 | 106 | module.exports = new LocalizationManager(); 107 | -------------------------------------------------------------------------------- /build/server/locales/en.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "default user name": "Someone", 3 | "email sharing subject": "%{displayName} shared \"%{name}\" with you", 4 | "email change subject": "%{displayName}'s Cozy: folder \"%{itemName}\" changed", 5 | "notification new file": "%{who} uploaded file %{fileName} into %{folderName}", 6 | "file": "file", 7 | "folder": "folder", 8 | "view file": "View file", 9 | "view folder": "Browse folder", 10 | "link file content": "Click here to view this file", 11 | "link folder content": "Click here to browse this folder", 12 | "404 headline": "Oops, this file cannot be found.", 13 | "404 option a": "You have mistyped the URL address", 14 | "404 option separator": "or", 15 | "404 option b": "The Cozy's owner may have deleted the related file", 16 | "404 clippy sorry": "Sorry, I couldn't find the file you are looking for", 17 | "404 clippy contact": "You may want to contact the Cozy's owner!" 18 | } 19 | ; 20 | -------------------------------------------------------------------------------- /build/server/locales/fr.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "default user name": "Quelqu'un", 3 | "email sharing subject": "%{displayName} a partagé \"%{name}\" avec vous", 4 | "email change subject": "Cozy de %{displayName}: le dossier \"%{itemName}\" a changé", 5 | "notification new file": "%{who} a ajouté le fichier %{fileName} dans le dossier %{folderName}", 6 | "file": "fichier", 7 | "folder": "dossier", 8 | "view file": "Afficher le fichier", 9 | "view folder": "Parcourir le dossier", 10 | "link file content": "Cliquez ici pour voir ce fichier", 11 | "link folder content": "Cliquez ici pour naviguer dans ce dossier", 12 | "404 headline": "Oups, ce fichier n'a pas pu être trouvé.", 13 | "404 option a": "Vous avez fait une erreur en tapant l'adresse URL", 14 | "404 option separator": "ou", 15 | "404 option b": "Le propriétaire de ce Cozy a supprimé le fichier correspondant", 16 | "404 clippy sorry": "Désolé, je n'ai pas pu trouver le fichier que vous cherchez", 17 | "404 clippy contact": "Vous pouvez toujours contacter le propriétaire de ce Cozy !" 18 | } 19 | ; 20 | -------------------------------------------------------------------------------- /build/server/middlewares/errors.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var logger, util; 3 | 4 | util = require('util'); 5 | 6 | logger = require('printit')({ 7 | date: true 8 | }); 9 | 10 | module.exports = function(err, req, res, next) { 11 | var header, message, ref, statusCode, templateName, value; 12 | statusCode = err.status || 500; 13 | message = err instanceof Error ? err.message : err.error; 14 | message = message || 'Server error occurred'; 15 | if ((err.headers != null) && Object.keys(err.headers).length > 0) { 16 | ref = err.headers; 17 | for (header in ref) { 18 | value = ref[header]; 19 | res.set(header, value); 20 | } 21 | } 22 | if ((err.template != null) && (req != null ? req.accepts('html') : void 0) === 'html') { 23 | templateName = "" + err.template.name; 24 | res.render(templateName, err.template.params, function(err, html) { 25 | return res.status(statusCode).send(html); 26 | }); 27 | } else { 28 | res.status(statusCode).send({ 29 | error: message 30 | }); 31 | } 32 | if (err instanceof Error) { 33 | logger.error(err.message); 34 | return logger.error(err.stack); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /build/server/middlewares/public_auth.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var File, Folder, sharing; 3 | 4 | Folder = require('../models/folder'); 5 | 6 | File = require('../models/file'); 7 | 8 | sharing = require('../helpers/sharing'); 9 | 10 | module.exports.checkClearance = function(permission, type) { 11 | return function(req, res, next) { 12 | var element; 13 | if (req.folder != null) { 14 | element = new Folder(req.folder); 15 | } else if (req.file != null) { 16 | element = new File(req.file); 17 | } else if (type === 'folder') { 18 | element = new Folder(req.body); 19 | } else { 20 | element = new File(req.body); 21 | } 22 | return sharing.checkClearance(element, req, permission, function(authorized, rule) { 23 | var err; 24 | if (authorized) { 25 | if (rule != null) { 26 | req.guestEmail = rule.email; 27 | req.guestId = rule.contactid; 28 | } 29 | return next(); 30 | } else { 31 | err = new Error('You cannot access this resource'); 32 | err.status = 404; 33 | err.template = { 34 | name: '404', 35 | params: { 36 | localization: require('../lib/localization_manager'), 37 | isPublic: true 38 | } 39 | }; 40 | return next(err); 41 | } 42 | }); 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /build/server/models/binary.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Binary, cozydb; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | module.exports = Binary = cozydb.getModel('Binary', cozydb.NoSchema); 7 | -------------------------------------------------------------------------------- /build/server/models/contact.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Contact, DataPoint, cozydb, 3 | extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, 4 | hasProp = {}.hasOwnProperty; 5 | 6 | cozydb = require('cozydb'); 7 | 8 | DataPoint = (function(superClass) { 9 | extend(DataPoint, superClass); 10 | 11 | function DataPoint() { 12 | return DataPoint.__super__.constructor.apply(this, arguments); 13 | } 14 | 15 | DataPoint.schema = { 16 | name: String, 17 | value: String, 18 | type: String 19 | }; 20 | 21 | return DataPoint; 22 | 23 | })(cozydb.Model); 24 | 25 | module.exports = Contact = cozydb.getModel('Contact', { 26 | fn: String, 27 | n: String, 28 | datapoints: [DataPoint] 29 | }); 30 | -------------------------------------------------------------------------------- /build/server/models/requests.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var cozydb; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | module.exports = { 7 | file: { 8 | all: cozydb.defaultRequests.all, 9 | byTag: cozydb.defaultRequests.tags, 10 | byFolder: cozydb.defaultRequests.by('path'), 11 | byFullPath: function(doc) { 12 | return emit(doc.path + '/' + doc.name, doc); 13 | } 14 | }, 15 | folder: { 16 | all: cozydb.defaultRequests.all, 17 | byTag: cozydb.defaultRequests.tags, 18 | byFolder: cozydb.defaultRequests.by('path'), 19 | byFullPath: function(doc) { 20 | return emit(doc.path + '/' + doc.name, doc); 21 | } 22 | }, 23 | contact: { 24 | all: cozydb.defaultRequests.all 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /build/server/models/user.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var cozydb, getUserInfo, hideEmail; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | hideEmail = function(email) { 7 | return email.split('@')[0].replace('.', ' ').replace('-', ' '); 8 | }; 9 | 10 | getUserInfo = function(callback) { 11 | return cozydb.api.getCozyUser(function(err, user) { 12 | var name, ref, words; 13 | if (err) { 14 | return callback(err); 15 | } 16 | name = ((ref = user.public_name) != null ? ref.length : void 0) ? user.public_name : (words = hideEmail(user.email).split(' '), words.map(function(word) { 17 | return word[0].toUpperCase() + word.slice(1); 18 | }).join(' ')); 19 | return callback(null, { 20 | name: name, 21 | email: user.email 22 | }); 23 | }); 24 | }; 25 | 26 | module.exports.getUserInfo = getUserInfo; 27 | 28 | module.exports.getDisplayName = function(callback) { 29 | return getUserInfo(function(err, user) { 30 | if (err) { 31 | return callback(err); 32 | } 33 | return callback(null, user.name); 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /build/server/views/404.js: -------------------------------------------------------------------------------- 1 | var jade = require('jade/runtime'); 2 | module.exports = function template(locals) { 3 | var buf = []; 4 | var jade_mixins = {}; 5 | var jade_interp; 6 | ;var locals_for_with = (locals || {});(function (localization) { 7 | buf.push("404 - File not found

" + (jade.escape(null == (jade_interp = localization.t('404 headline')) ? "" : jade_interp)) + "

" + (jade.escape(null == (jade_interp = localization.t('404 option a')) ? "" : jade_interp)) + "

" + (jade.escape(null == (jade_interp = localization.t('404 option separator')) ? "" : jade_interp)) + "

" + (jade.escape(null == (jade_interp = localization.t('404 option b')) ? "" : jade_interp)) + "

");}.call(this,"localization" in locals_for_with?locals_for_with.localization:typeof localization!=="undefined"?localization:undefined));;return buf.join(""); 8 | } -------------------------------------------------------------------------------- /build/server/views/en/notifmail.js: -------------------------------------------------------------------------------- 1 | var jade = require('jade/runtime'); 2 | module.exports = function template(locals) { 3 | var buf = []; 4 | var jade_mixins = {}; 5 | var jade_interp; 6 | ;var locals_for_with = (locals || {});(function (displayName, localization, name, url) { 7 | buf.push("
The content of \"" + (jade.escape((jade_interp = name) == null ? '' : jade_interp)) + "\" has changed.
" + (jade.escape((jade_interp = localization.t('link folder content')) == null ? '' : jade_interp)) + " !
To unsubscribe from these notifications, click here.
Sent from " + (jade.escape((jade_interp = displayName) == null ? '' : jade_interp)) + "'s Cozy.
");}.call(this,"displayName" in locals_for_with?locals_for_with.displayName:typeof displayName!=="undefined"?displayName:undefined,"localization" in locals_for_with?locals_for_with.localization:typeof localization!=="undefined"?localization:undefined,"name" in locals_for_with?locals_for_with.name:typeof name!=="undefined"?name:undefined,"url" in locals_for_with?locals_for_with.url:typeof url!=="undefined"?url:undefined));;return buf.join(""); 8 | } -------------------------------------------------------------------------------- /build/server/views/fr/notifmail.js: -------------------------------------------------------------------------------- 1 | var jade = require('jade/runtime'); 2 | module.exports = function template(locals) { 3 | var buf = []; 4 | var jade_mixins = {}; 5 | var jade_interp; 6 | ;var locals_for_with = (locals || {});(function (displayName, localization, name, url) { 7 | buf.push("
Le contenu du dossier \"" + (jade.escape((jade_interp = name) == null ? '' : jade_interp)) + "\" a changé.
" + (jade.escape((jade_interp = localization.t('link folder content')) == null ? '' : jade_interp)) + " !
Pour vous désabonner de ces notifications, cliquez ici.
Envoyé depuis le Cozy de " + (jade.escape((jade_interp = displayName) == null ? '' : jade_interp)) + ".
");}.call(this,"displayName" in locals_for_with?locals_for_with.displayName:typeof displayName!=="undefined"?displayName:undefined,"localization" in locals_for_with?locals_for_with.localization:typeof localization!=="undefined"?localization:undefined,"name" in locals_for_with?locals_for_with.name:typeof name!=="undefined"?name:undefined,"url" in locals_for_with?locals_for_with.url:typeof url!=="undefined"?url:undefined));;return buf.join(""); 8 | } -------------------------------------------------------------------------------- /build/server/views/index.js: -------------------------------------------------------------------------------- 1 | var jade = require('jade/runtime'); 2 | module.exports = function template(locals) { 3 | var buf = []; 4 | var jade_mixins = {}; 5 | var jade_interp; 6 | ;var locals_for_with = (locals || {});(function (imports) { 7 | buf.push("Cozy - Files");}.call(this,"imports" in locals_for_with?locals_for_with.imports:typeof imports!=="undefined"?imports:undefined));;return buf.join(""); 8 | } -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /client/app/application.coffee: -------------------------------------------------------------------------------- 1 | FileCollection = require './collections/files' 2 | UploadQueue = require './collections/upload_queue' 3 | File = require './models/file' 4 | SocketListener = require '../lib/socket' 5 | Gallery = require './views/gallery' 6 | 7 | ### 8 | Initialize the model and start the actual code 9 | ### 10 | module.exports = 11 | 12 | initialize: -> 13 | 14 | # if the application is browsed by a guest or not 15 | # we use that in various places of the application (as few as possible) 16 | @isPublic = window.location.pathname.indexOf('/public/') is 0 17 | 18 | # the base collection holds all the files and folders of the application 19 | # this collection is a local cache : the content of already visited 20 | # folders will remain in this collection. 21 | @baseCollection = new FileCollection() 22 | 23 | # queue to allow new uploads while uploading 24 | @uploadQueue = new UploadQueue @baseCollection 25 | 26 | @socket = new SocketListener() 27 | @socket.watch @baseCollection 28 | 29 | Router = require 'router' 30 | @router = new Router() 31 | 32 | # Generate the root folder 33 | # In shared area there are more properties because the root is an 34 | # actual folder 35 | if window.rootFolder? 36 | @root = new File window.rootFolder 37 | @root.canUpload = window.canUpload or false 38 | @root.publicNotificationsEnabled = window.publicNofications or false 39 | @root.publicKey = window.publicKey or "" 40 | else 41 | # Fake folder to describe the root 42 | @root = new File 43 | id: "root" 44 | path: "" 45 | name: t 'root folder name' 46 | type: "folder" 47 | @baseCollection.add @root 48 | # for easy debugging in browser (and dirty tricks) 49 | window.app = @ 50 | 51 | Backbone.history.start() 52 | 53 | # prepare the photo gallery 54 | window.app.gallery = new Gallery() 55 | 56 | # Makes this object immuable. 57 | Object.freeze? @ 58 | 59 | document.body.addEventListener 'click', (event) => 60 | if event.target.tagName is 'BODY' 61 | @baseCollection.forEach (model) -> 62 | model.setSelectedViewState false 63 | 64 | -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/ODFViewerPlugin.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013-2014 KO GmbH 3 | * 4 | * @licstart 5 | * This file is part of WebODF. 6 | * 7 | * WebODF is free software: you can redistribute it and/or modify it 8 | * under the terms of the GNU Affero General Public License (GNU AGPL) 9 | * as published by the Free Software Foundation, either version 3 of 10 | * the License, or (at your option) any later version. 11 | * 12 | * WebODF is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with WebODF. If not, see . 19 | * @licend 20 | * 21 | * @source: http://www.webodf.org/ 22 | * @source: https://github.com/kogmbh/WebODF/ 23 | */ 24 | 25 | @namespace cursor url(urn:webodf:names:cursor); 26 | 27 | .caret { 28 | opacity: 0 !important; 29 | } 30 | -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/PDFViewerPlugin.css: -------------------------------------------------------------------------------- 1 | .page { 2 | margin: 7px auto 7px auto; 3 | position: relative; 4 | overflow: hidden; 5 | background-clip: content-box; 6 | background-color: white; 7 | 8 | box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 9 | -webkit-box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 10 | -moz-box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 11 | -ms-box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 12 | -o-box-shadow: 0px 0px 7px rgba(0, 0, 0, 0.75); 13 | } 14 | 15 | .textLayer { 16 | position: absolute; 17 | left: 0; 18 | top: 0; 19 | right: 0; 20 | bottom: 0; 21 | color: #000; 22 | font-family: sans-serif; 23 | overflow: hidden; 24 | } 25 | 26 | .textLayer > div { 27 | color: transparent; 28 | position: absolute; 29 | line-height: 1; 30 | white-space: pre; 31 | cursor: text; 32 | } 33 | 34 | ::selection { background:rgba(0,0,255,0.3); } 35 | ::-moz-selection { background:rgba(0,0,255,0.3); } 36 | 37 | -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/example.local.css: -------------------------------------------------------------------------------- 1 | /* This is just a sample file with CSS rules. You should write your own @font-face declarations 2 | * to add support for your desired fonts. 3 | */ 4 | 5 | @font-face { 6 | font-family: 'Novecentowide Book'; 7 | src: url("/ViewerJS/fonts/Novecentowide-Bold-webfont.eot"); 8 | src: url("/ViewerJS/fonts/Novecentowide-Bold-webfont.eot?#iefix") format("embedded-opentype"), 9 | url("/ViewerJS/fonts/Novecentowide-Bold-webfont.woff") format("woff"), 10 | url("/fonts/Novecentowide-Bold-webfont.ttf") format("truetype"), 11 | url("/fonts/Novecentowide-Bold-webfont.svg#NovecentowideBookBold") format("svg"); 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | 16 | @font-face { 17 | font-family: 'exotica'; 18 | src: url('/ViewerJS/fonts/Exotica-webfont.eot'); 19 | src: url('/ViewerJS/fonts/Exotica-webfont.eot?#iefix') format('embedded-opentype'), 20 | url('/ViewerJS/fonts/Exotica-webfont.woff') format('woff'), 21 | url('/ViewerJS/fonts/Exotica-webfont.ttf') format('truetype'), 22 | url('/ViewerJS/fonts/Exotica-webfont.svg#exoticamedium') format('svg'); 23 | font-weight: normal; 24 | font-style: normal; 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/kogmbh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/kogmbh.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/nlnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/nlnet.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/texture.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/toolbarButton-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/toolbarButton-download.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/toolbarButton-fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/toolbarButton-fullscreen.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/toolbarButton-menuArrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/toolbarButton-menuArrows.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/toolbarButton-pageDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/toolbarButton-pageDown.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/toolbarButton-pageUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/toolbarButton-pageUp.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/toolbarButton-presentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/toolbarButton-presentation.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/toolbarButton-zoomIn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/toolbarButton-zoomIn.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/images/toolbarButton-zoomOut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/ViewerJS/images/toolbarButton-zoomOut.png -------------------------------------------------------------------------------- /client/app/assets/ViewerJS/pdfjsversion.js: -------------------------------------------------------------------------------- 1 | var /**@const{!string}*/pdfjs_version = "d45d7bc"; 2 | -------------------------------------------------------------------------------- /client/app/assets/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/android-chrome-144x144.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/android-chrome-36x36.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/android-chrome-48x48.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/android-chrome-72x72.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/android-chrome-96x96.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /client/app/assets/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #8cb1ff 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /client/app/assets/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /client/app/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/favicon-16x16.png -------------------------------------------------------------------------------- /client/app/assets/favicon-194x194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/favicon-194x194.png -------------------------------------------------------------------------------- /client/app/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/favicon-32x32.png -------------------------------------------------------------------------------- /client/app/assets/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/favicon-96x96.png -------------------------------------------------------------------------------- /client/app/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/favicon.ico -------------------------------------------------------------------------------- /client/app/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/mavenpro-bold.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/mavenpro-bold.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/mavenpro-regular.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/mavenpro-regular.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/sourcesanspro-bold-italic.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/sourcesanspro-bold-italic.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/sourcesanspro-bold.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/sourcesanspro-bold.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/sourcesanspro-italic.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/sourcesanspro-italic.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/sourcesanspro-regular.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/fonts/sourcesanspro-regular.woff2 -------------------------------------------------------------------------------- /client/app/assets/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | jQuery, Modernizr, 16 | Lodash, Backbone, 17 | Handlebars, Coffeescript, 18 | Stylus, Brunch 19 | -------------------------------------------------------------------------------- /client/app/assets/icons/main_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/icons/main_icon.png -------------------------------------------------------------------------------- /client/app/assets/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/images/.gitkeep -------------------------------------------------------------------------------- /client/app/assets/images/Melamine-wood-003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/images/Melamine-wood-003.png -------------------------------------------------------------------------------- /client/app/assets/images/Melamine-wood-004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/images/Melamine-wood-004.png -------------------------------------------------------------------------------- /client/app/assets/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/images/background.jpg -------------------------------------------------------------------------------- /client/app/assets/images/clippy-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/images/clippy-map.png -------------------------------------------------------------------------------- /client/app/assets/images/cozy-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/images/cozy-logo.png -------------------------------------------------------------------------------- /client/app/assets/images/defaultpicture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/images/defaultpicture.png -------------------------------------------------------------------------------- /client/app/assets/images/magnifier.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | magnifier 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/app/assets/images/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/app/assets/images/sprite-files.svg: -------------------------------------------------------------------------------- 1 | sprite-filesCreated with Sketch. -------------------------------------------------------------------------------- /client/app/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cozy Files", 3 | "icons": [ 4 | { 5 | "src": "\/apps\/files\/android-chrome-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/apps\/files\/android-chrome-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/apps\/files\/android-chrome-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/apps\/files\/android-chrome-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/apps\/files\/android-chrome-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/apps\/files\/android-chrome-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /client/app/assets/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/mstile-144x144.png -------------------------------------------------------------------------------- /client/app/assets/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/mstile-150x150.png -------------------------------------------------------------------------------- /client/app/assets/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/mstile-310x150.png -------------------------------------------------------------------------------- /client/app/assets/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/mstile-310x310.png -------------------------------------------------------------------------------- /client/app/assets/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/assets/mstile-70x70.png -------------------------------------------------------------------------------- /client/app/assets/stylesheets/clippy.css: -------------------------------------------------------------------------------- 1 | .clippy, .clippy-balloon { 2 | position: fixed; 3 | z-index: 1000; 4 | cursor: pointer; 5 | } 6 | 7 | .clippy-balloon { 8 | 9 | background: #FFC; 10 | color: black; 11 | padding: 8px; 12 | border: 1px solid black; 13 | border-radius: 5px; 14 | 15 | } 16 | 17 | .clippy-content { 18 | max-width: 200px; 19 | min-width: 120px; 20 | font-family: "Microsoft Sans", sans-serif; 21 | font-size: 10pt; 22 | } 23 | 24 | .clippy-tip { 25 | width: 10px; 26 | height: 16px; 27 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAgCAMAAAAlvKiEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRF///MAAAA////52QwgAAAAAN0Uk5T//8A18oNQQAAAGxJREFUeNqs0kEOwCAIRFHn3//QTUU6xMyyxii+jQosrTPkyPEM6IN3FtzIRk1U4dFeKWQiH6pRRowMVKEmvronEynkwj0uZJgR22+YLopPSo9P34wJSamLSU7lSIWLJU7NkNomNlhqxUeAAQC+TQLZyEuJBwAAAABJRU5ErkJggg==) no-repeat; 28 | position: absolute; 29 | } 30 | 31 | .clippy-top-left .clippy-tip { 32 | top: 100%; 33 | margin-top: 0px; 34 | left: 100%; 35 | margin-left: -50px; 36 | } 37 | 38 | .clippy-top-right .clippy-tip { 39 | top: 100%; 40 | margin-top: 0px; 41 | left: 0; 42 | margin-left: 50px; 43 | background-position: -10px 0; 44 | 45 | } 46 | 47 | .clippy-bottom-right .clippy-tip { 48 | top: 0; 49 | margin-top: -16px; 50 | left: 0; 51 | margin-left: 50px; 52 | background-position: -10px -16px; 53 | } 54 | 55 | .clippy-bottom-left .clippy-tip { 56 | top: 0; 57 | margin-top: -16px; 58 | left: 100%; 59 | margin-left: -50px; 60 | background-position: 0px -16px; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /client/app/collections/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/collections/.gitkeep -------------------------------------------------------------------------------- /client/app/initialize.coffee: -------------------------------------------------------------------------------- 1 | app = require 'application' 2 | 3 | $ -> 4 | jQuery.event.props.push 'dataTransfer' 5 | 6 | if window.domain is "false" 7 | app.domain = "#{window.location.origin}/public/files/" 8 | else 9 | app.domain = window.domain 10 | 11 | locale = window.locale or "en" # default locale 12 | moment.lang locale 13 | 14 | locales = {} 15 | try 16 | locales = require "locales/#{locale}" 17 | catch err 18 | locales = require "locales/en" 19 | 20 | polyglot = new Polyglot() 21 | 22 | # we give polyglot the data 23 | polyglot.extend locales 24 | 25 | # handy shortcut 26 | window.t = polyglot.t.bind polyglot 27 | 28 | # init plugins 29 | require("./utils/plugin_utils").init() 30 | 31 | # keeps count of operations in progress 32 | window.pendingOperations = 33 | upload: 0 34 | move: 0 35 | deletion: 0 36 | 37 | # launch the app 38 | app.initialize() 39 | 40 | # Asks the user if he tries to reload the page while an operation is in progress 41 | window.onbeforeunload = -> 42 | 43 | values = _.values window.pendingOperations 44 | sum = (a, b) -> a + b 45 | pendingOperationsNum = _.reduce values, sum, 0 46 | if pendingOperationsNum > 0 47 | return t 'confirmation reload' 48 | 49 | -------------------------------------------------------------------------------- /client/app/lib/base_view.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class BaseView extends Backbone.View 2 | 3 | template: -> 4 | 5 | initialize: -> 6 | 7 | getRenderData: -> 8 | model: @model?.toJSON() 9 | 10 | render: -> 11 | @beforeRender() 12 | @$el.html @template @getRenderData() 13 | @afterRender() 14 | return @ 15 | 16 | beforeRender: -> 17 | 18 | afterRender: -> 19 | 20 | destroy: -> @remove() 21 | -------------------------------------------------------------------------------- /client/app/lib/client.coffee: -------------------------------------------------------------------------------- 1 | # Make ajax request more easy to do. 2 | exports.request = (type, url, data, callback) -> 3 | $.ajax 4 | type: type 5 | url: url 6 | data: if data? then JSON.stringify data else null 7 | contentType: "application/json" 8 | dataType: "json" 9 | success: (data) -> 10 | callback null, data if callback? 11 | error: (data) -> 12 | if data.status in [200, 201, 204, 304] 13 | callback null, data if callback? 14 | else if data? and data.msg? and callback? 15 | callback new Error data.msg 16 | else if callback? 17 | callback new Error "Server error occured" 18 | 19 | # Sends a get request with data as body 20 | # Expected callbacks: success and error 21 | exports.get = (url, callbacks) -> 22 | exports.request "GET", url, null, callbacks 23 | 24 | # Sends a post request with data as body 25 | # Expected callbacks: success and error 26 | exports.post = (url, data, callbacks) -> 27 | exports.request "POST", url, data, callbacks 28 | 29 | # Sends a put request with data as body 30 | # Expected callbacks: success and error 31 | exports.put = (url, data, callbacks) -> 32 | exports.request "PUT", url, data, callbacks 33 | 34 | # Sends a delete request with data as body 35 | # Expected callbacks: success and error 36 | exports.del = (url, callbacks) -> 37 | exports.request "DELETE", url, null, callbacks 38 | -------------------------------------------------------------------------------- /client/app/lib/folder_helpers.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | 3 | removeTralingSlash: (path) -> 4 | if path.slice(-1) is '/' 5 | return path.slice(0, -1) 6 | return path 7 | 8 | dirName: (path) -> 9 | return path.split('/')[...-1].join('/') 10 | 11 | 12 | getFolderPathParts: (path) -> 13 | return path.split('/').filter (x) -> x # remove empty last part 14 | 15 | nestedDirs: (fileList) -> 16 | 17 | # list of folders to create 18 | dirs = [] 19 | 20 | # cache the key of folders to prevent useless loops 21 | addedPath = [] 22 | 23 | # make an object with keys paths, values nest level 24 | # from each files we build the tree to recreate it 25 | for file in fileList 26 | relPath = file.relativePath or file.mozRelativePath or file.webkitRelativePath 27 | parents = relPath.slice(0, relPath.lastIndexOf(file.name)) 28 | 29 | # get an array of the folders making the file path 30 | foldersOfPath = parents.split('/')[...-1] 31 | while foldersOfPath.length > 0 32 | parent = foldersOfPath.join '/' 33 | if not (parent in addedPath) 34 | dirs.push path: parent, depth: foldersOfPath.length 35 | addedPath.push parent 36 | foldersOfPath.pop() 37 | else 38 | break 39 | 40 | # put them in a list, sorted by nest level 41 | dirs.sort (a, b) -> 42 | return a.depth - b.depth 43 | 44 | return (dir.path for dir in dirs) 45 | -------------------------------------------------------------------------------- /client/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/app/models/.gitkeep -------------------------------------------------------------------------------- /client/app/router.coffee: -------------------------------------------------------------------------------- 1 | app = require 'application' 2 | File = require './models/file' 3 | FileCollection = require './collections/files' 4 | FolderView = require './views/folder' 5 | PublicFolderView = require './views/public_folder' 6 | 7 | ### 8 | Binds routes to code actions. 9 | This is also used as a controller to initialize views and perform data fetching 10 | ### 11 | module.exports = class Router extends Backbone.Router 12 | 13 | folderView: null 14 | 15 | routes: 16 | '': 'main' 17 | 'folders/:folderid' : 'folder' 18 | 'search/:query' : 'search' 19 | 20 | main: -> 21 | rootID = app.root.get 'id' 22 | @_loadFolderView rootID 23 | 24 | folder: (id) -> @_loadFolderView id 25 | 26 | search: (query) -> 27 | folder = new File 28 | id: query 29 | type: "search" 30 | name: "#{t('breadcrumbs search title')} '#{query}'" 31 | 32 | @folderView.spin() if @folderView? 33 | folder.fetchContent (err, content) => 34 | collection = new FileCollection content 35 | 36 | # we don't re-render the view to prevent the search field 37 | # from being reset 38 | if @folderView? 39 | @folderView.spin false 40 | @folderView.updateSearch folder, collection 41 | else 42 | @_renderFolderView folder, collection, query 43 | 44 | # get the data and render the view 45 | _loadFolderView: (folderID) -> 46 | # add the spinner during folder change 47 | @folderView.spin() if @folderView? 48 | 49 | # retrieve folder info and content from data source or cache 50 | app.baseCollection.getByFolder folderID, (err, folder, collection) => 51 | if err? then console.log err 52 | else 53 | # sort by creation date the folder where phones photo are 54 | # uploaded 55 | path = folder.getRepository() 56 | if path in ['/Appareils photo', '/Photos from devices'] 57 | collection.type = 'lastModification' 58 | collection.order = 'desc' 59 | collection.sort() 60 | @_renderFolderView folder, collection 61 | 62 | # process the actual render 63 | _renderFolderView: (folder, collection, query = '') -> 64 | if @folderView? 65 | @folderView.destroy() 66 | # because destroying the view also removes the element 67 | $('html').append $ '' 68 | 69 | # we generate a mixed collection with content & uploads 70 | @folderView = @_getFolderView 71 | model: folder 72 | collection: collection 73 | baseCollection: app.baseCollection 74 | breadcrumbs: app.breadcrumbs 75 | uploadQueue: app.uploadQueue 76 | query: query 77 | @folderView.render() 78 | 79 | # factory to get the proper folder object based on mode (shared or not) 80 | _getFolderView: (params) -> 81 | if app.isPublic 82 | return new PublicFolderView _.extend params, rootFolder: app.root 83 | else 84 | return new FolderView params 85 | 86 | -------------------------------------------------------------------------------- /client/app/views/breadcrumbs.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../lib/base_view' 2 | 3 | module.exports = class BreadcrumbsView extends BaseView 4 | 5 | itemview: require './templates/breadcrumbs_element' 6 | tagName: "ul" 7 | 8 | # collection is a simple array, not a backbone collection 9 | render: -> 10 | # temporary trick to prevent public area 11 | # to display the root (shared folder) twice in shared mode 12 | @collection.shift() unless @collection[0].id is 'root' 13 | 14 | opacity = if @collection.length is 1 then '0.5' else '1' 15 | @$el.css 'opacity', opacity 16 | 17 | @$el.empty() 18 | for folder in @collection 19 | @$el.append @itemview model: folder 20 | @ 21 | 22 | -------------------------------------------------------------------------------- /client/app/views/gallery.coffee: -------------------------------------------------------------------------------- 1 | 2 | ###* 3 | * this module is in charge of displaying a photo gallery when an image is 4 | * clicked 5 | ### 6 | module.exports = class Gallery 7 | 8 | ###* 9 | * will open the diaporama when a photo is clicked 10 | * @param {BackboneModel} modelClicked Model of the file clicked 11 | ### 12 | show: (modelClicked) -> 13 | 14 | # 1/ preprare the div of a fake thumbnail (input for baguettebox.js) 15 | gallery = document.getElementById('gallery') 16 | if gallery is null 17 | gallery = document.createElement('div') 18 | gallery.id = 'gallery' 19 | gallery.style.display = 'none' 20 | document.body.appendChild gallery 21 | else 22 | gallery.innerHTML = '' 23 | 24 | # 2/ populate the div with the links to the photo to display 25 | a_toSimulateClick = null 26 | window.app.router.folderView.collection.forEach (model)=> 27 | if !model.isImage() 28 | return 29 | a = document.createElement('a') 30 | a.href = model.getScreenUrl() 31 | gallery.appendChild a 32 | if model is modelClicked 33 | a_toSimulateClick = a 34 | 35 | # 3/ run baguetteBox 36 | window.baguetteBox.run '#gallery', 37 | captions : true 38 | buttons : 'auto' 39 | async : false 40 | preload : 2 41 | animation : 'slideIn' 42 | 43 | # 4/ launch the display of the diaporama. For that we have to simulate 44 | # a click on a thumbnail. 45 | event = document.createEvent('MouseEvent') 46 | event.initMouseEvent 'click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null 47 | event.preventDefault() 48 | a_toSimulateClick.dispatchEvent event 49 | 50 | return 51 | 52 | 53 | -------------------------------------------------------------------------------- /client/app/views/modal.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../lib/base_view' 2 | 3 | module.exports = class ModalView extends BaseView 4 | 5 | id: "dialog-modal" 6 | className: "modal fade" 7 | attributes: 8 | 'tab-index': -1 9 | template: require './templates/modal' 10 | value: 0 11 | 12 | events: -> 13 | "click #modal-dialog-no" : "onNo" 14 | "click #modal-dialog-yes" : "onYes" 15 | 16 | constructor: (@title, @msg, @yes, @no, @cb, @hideOnYes=true) -> 17 | super() 18 | 19 | initialize: -> 20 | @$el.on 'hidden.bs.modal', @close.bind(@) 21 | 22 | @render() 23 | @show() 24 | 25 | onYes: -> 26 | @cb true if @cb 27 | @hide() 28 | 29 | onNo: -> 30 | @cb false if @cb 31 | @hide() if @hideOnYes 32 | 33 | onShow: -> 34 | 35 | close: -> 36 | setTimeout @destroy.bind(@), 500 37 | 38 | show: -> 39 | @$el.modal 'show' 40 | @$el.trigger 'show' 41 | 42 | hide: -> 43 | @$el.modal 'hide' 44 | @$el.trigger 'hide' 45 | 46 | render: -> 47 | @$el.append @template title: @title, msg: @msg, yes: @yes, no: @no 48 | $("body").append @$el 49 | @afterRender() 50 | @ 51 | 52 | module.exports.error = (code, cb) -> 53 | new ModalView t("modal error"), code, t("modal ok"), false, cb 54 | -------------------------------------------------------------------------------- /client/app/views/modal_conflict.coffee: -------------------------------------------------------------------------------- 1 | Modal = require "./modal" 2 | client = require "../lib/client" 3 | 4 | module.exports = class ModalConflictView extends Modal 5 | 6 | conflictTemplate: -> 7 | rememberLabel = t 'overwrite modal remember label' 8 | return """ 9 |
10 |

#{t 'overwrite modal content', fileName: @model.get 'name'}

11 |

12 | 13 | 14 |

15 |
16 | """ 17 | 18 | constructor: (@model, @callback) -> 19 | super t('overwrite modal title'), '', \ 20 | t('overwrite modal yes button'), \ 21 | t('overwrite modal no button'), \ 22 | @confirmCallback 23 | 24 | confirmCallback: (confirm) -> 25 | rememberChoice = @$('#rememberChoice').prop 'checked' 26 | rememberedChoice = confirm if rememberChoice 27 | @callback confirm, rememberedChoice 28 | 29 | afterRender: -> 30 | @conflictForm = $ @conflictTemplate() 31 | @$el.find('.modal-body').append @conflictForm 32 | 33 | -------------------------------------------------------------------------------- /client/app/views/public_folder.coffee: -------------------------------------------------------------------------------- 1 | FolderView = require './folder' 2 | client = require '../lib/client' 3 | 4 | module.exports = class PublicFolderView extends FolderView 5 | 6 | events: -> _.extend super, 7 | 'click #notifications': 'onToggleNotificationClicked' 8 | 9 | 10 | initialize: (options) -> 11 | super options 12 | @rootFolder = options.rootFolder 13 | 14 | afterRender: -> 15 | super() 16 | 17 | # we use those class to show/hide stuff via CSS (buttons, controls, etc.) 18 | classes = ' public' 19 | classes += ' can-upload' if @rootFolder.canUpload 20 | @$el.addClass classes 21 | 22 | getRenderData: -> 23 | _.extend super(), 24 | isPublic: true 25 | areNotificationsEnabled: @rootFolder.publicNotificationsEnabled 26 | hasPublicKey: @rootFolder.publicKey.length > 0 27 | 28 | # Enable or disable guest notifications 29 | onToggleNotificationClicked: -> 30 | 31 | # Notifications are only handled for the root folder 32 | key = window.location.search 33 | url = "#{@model.urlRoot()}#{@rootFolder.id}/notifications#{key}" 34 | 35 | # toggle notifications button and persist state in database 36 | if @rootFolder.publicNotificationsEnabled 37 | @rootFolder.publicNotificationsEnabled = false 38 | @$('#notifications').html ' ' 39 | @$('#notifications').spin 'tiny' 40 | client.put url, notificationsState: false, (err) => 41 | @$('#notifications').spin false 42 | unless err? 43 | @$('#notifications').html t 'notifications disabled' 44 | @$('#notifications').removeClass 'toggled' 45 | else 46 | @rootFolder.publicNotificationsEnabled = true 47 | @$('#notifications').html ' ' 48 | @$('#notifications').spin 'tiny' 49 | client.put url, notificationsState: true, (err) => 50 | @$('#notifications').spin false 51 | unless err? 52 | @$('#notifications').html t 'notifications enabled' 53 | @$('#notifications').addClass 'toggled' -------------------------------------------------------------------------------- /client/app/views/styles/_colors.styl: -------------------------------------------------------------------------------- 1 | // blue 2 | basecolor = #34A6FF 3 | basecolor-alpha = rgba(49, 156, 256, 0.5) 4 | 5 | // green 6 | contrastcolor = #5C5 7 | 8 | // orange 9 | actioncolor = #fe8800 10 | actioncolor-alpha = rgba(254,128,0,0.5) 11 | 12 | // gray and dark gray 13 | lightcolor = #EEE 14 | lightorange = #FFC47F 15 | lightblue = #CBE4FB 16 | mediumcolor = #BDBDBD 17 | mediumcolor-contrast = #428bca 18 | darkcolor = #444 19 | dangercolor = #ea202d 20 | 21 | // 50 (well, in fact, mostly 8) shades of grey 22 | grey-01 = #ECEFF3 23 | grey-02 = #DFE4E9 24 | grey-03 = #C8D5DF 25 | grey-04 = #ACB8C5 26 | grey-05 = #92A0B2 27 | grey-06 = #748192 28 | grey-07 = #4F5B69 29 | grey-08 = #32363F 30 | -------------------------------------------------------------------------------- /client/app/views/styles/_cozy.styl: -------------------------------------------------------------------------------- 1 | orange = #fe8800 2 | blue = #34A6FF 3 | bluegreen = #34A6BB 4 | 5 | 6 | a 7 | color: blue 8 | 9 | a:hover 10 | color: orange 11 | 12 | input 13 | select 14 | textarea 15 | border: 1px solid blue 16 | border-radius: 0 17 | font-family: 'Source Sans Pro', sans-serif 18 | 19 | input:focus 20 | select:focus 21 | textarea:focus 22 | border: 1px solid blue 23 | 24 | input:hover 25 | select:hover 26 | textarea:hover 27 | border: 1px solid basecolor 28 | 29 | input:active 30 | select:active 31 | textarea:active 32 | box-shadow: none 33 | 34 | 35 | .button 36 | border: 0 37 | background: basecolor 38 | color: white 39 | box-shadow: none 40 | text-shadow: none 41 | border-radius: 0 42 | font-family: 'Source Sans Pro', sans-serif 43 | font-weight: bold 44 | padding: 10px 45 | cursor: pointer 46 | border-radius: 5px 47 | 48 | .button:hover 49 | background darken(basecolor, 20%) 50 | border: 0 51 | color: white 52 | text-decoration: none 53 | 54 | .button:active 55 | background: basecolor - 3% 56 | border: 0 57 | box-shadow: 1px 3px 2px rgba(0, 0, 0, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.0) 58 | color: white 59 | outline: 0 60 | 61 | .button:focus 62 | outline: 0 63 | 64 | .button.disabled 65 | .button.disabled:hover 66 | .button.disabled:active 67 | .button.disabled:focus 68 | background-color: grey 69 | padding: 10px 70 | cursor: default 71 | text-decoration: none 72 | 73 | .button.toggled 74 | background-color: blue - 20% 75 | box-shadow: 1px 3px 2px rgba(0, 0, 0, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.0) 76 | 77 | .minor-button 78 | padding: 10px 79 | 80 | .modal-header 81 | font-family: 'Maven Pro', sans-serif 82 | font-size: 26px 83 | padding: 20px 84 | border-top-left-radius: 5px 85 | border-top-right-radius: 5px 86 | 87 | .modal-body 88 | font-family: 'Source Sans Pro', sans-serif 89 | 90 | input 91 | select 92 | textarea 93 | border: 1px solid #CCC 94 | border-radius: 0 95 | font-family: 'Source Sans Pro', sans-serif 96 | 97 | &:hover 98 | border: 1px solid orange 99 | 100 | &:focus 101 | border: 1px solid blue 102 | box-shadow: none 103 | 104 | &:active 105 | border: 1px solid #CCC 106 | box-shadow: none 107 | -------------------------------------------------------------------------------- /client/app/views/styles/_icons.styl: -------------------------------------------------------------------------------- 1 | .action-icon 2 | display block 3 | width 20px 4 | height 20px 5 | background-image url("../images/sprite-files.svg") 6 | 7 | &.file-uploader 8 | background-position 0 0 9 | 10 | &.folder-uploader 11 | background-position -20px 0 12 | 13 | &.folder-new 14 | background-position -40px 0 15 | 16 | 17 | // Files Types 18 | .icon-type 19 | position relative 20 | flex none 21 | margin 0 1.5rem 0 0 22 | width 32px 23 | height 32px 24 | background-image url("../images/sprite-files-type.svg") 25 | 26 | .thumb 27 | display none 28 | width 32px 29 | height 32px 30 | 31 | 32 | .fa-globe 33 | display none 34 | z-index 10 35 | position absolute 36 | top 19px 37 | left -6px 38 | padding 0.1em 39 | background-color #fff 40 | border-radius 50% 41 | font-size 14px 42 | color blue - 20% 43 | 44 | &.shared 45 | .fa-globe 46 | display block 47 | 48 | &.type-folder 49 | background-position 0 0 50 | 51 | &.type-file 52 | background-position -32px 0 53 | 54 | &.type-archive 55 | background-position -64px 0 56 | 57 | &.type-code 58 | background-position -96px 0 59 | 60 | &.type-text 61 | background-position -128px 0 62 | 63 | &.type-presentation 64 | background-position -160px 0 65 | 66 | &.type-spreadsheet 67 | background-position -192px 0 68 | 69 | &.type-pdf 70 | background-position -224px 0 71 | 72 | &.type-binary 73 | background-position -256px 0 74 | 75 | &.type-audio 76 | background-position -288px 0 77 | 78 | &.type-video 79 | background-position -320px 0 80 | 81 | &.type-image 82 | background-position -352px 0 83 | 84 | &.type-thumb 85 | background-image none 86 | 87 | .thumb 88 | border 1px solid grey-06 89 | border-radius 2px 90 | background-color grey-06 91 | display block 92 | 93 | 94 | 95 | 96 | .edit-mode .icon-type 97 | // display the folder icon when creating a new folder 98 | display inline-block 99 | 100 | -------------------------------------------------------------------------------- /client/app/views/styles/_responsive.styl: -------------------------------------------------------------------------------- 1 | @media (max-width: (520/16)em) 2 | // Landscape phones and down 3 | 4 | body 5 | text-rendering optimizeSpeed 6 | 7 | .itemRow .caption 8 | font-size 14px 9 | 10 | div#affixbar 11 | #search-box 12 | display none 13 | 14 | .pull-right 15 | display block 16 | 17 | #crumbs ul li 18 | a 19 | padding 0.5em 0.3em 0.5em 1em 20 | 21 | &:first-child a 22 | padding-left 0.8em 23 | 24 | &:last-child a 25 | padding-right 0.6em 26 | 27 | 28 | .modal-dialog 29 | max-width: 100% 30 | width: 100% 31 | 32 | #share-list li select.change-perm 33 | display block 34 | 35 | #share-list img 36 | display: none 37 | 38 | #cozy-clearance-modal .input-group input 39 | width 75% 40 | 41 | #cozy-clearance-modal .input-group a.btn 42 | width 25% 43 | 44 | @media (max-width: 768px) 45 | // Landscape phone to portrait tablet 46 | body 47 | text-rendering optimizeSpeed 48 | 49 | .button-title-reponsive 50 | display none 51 | 52 | .type-column-cell 53 | .date-column-cell 54 | .size-column-cell 55 | display none 56 | 57 | 58 | .itemRow 59 | .tags 60 | display none 61 | 62 | .file-tags 63 | .file-delete 64 | .file-download 65 | .file-edit 66 | .file-share 67 | .file-move 68 | .file-preview 69 | display none 70 | 71 | .caption 72 | margin-right 0 73 | 74 | #crumbs 75 | ul 76 | li 77 | display none 78 | 79 | li:first-child 80 | li:last-child 81 | display inline 82 | 83 | #affixbar 84 | #search-box 85 | max-width 150px 86 | 87 | #upload-buttons a 88 | &.btn 89 | padding 6px 0.4em 90 | 91 | &#share-state 92 | &#button-new-folder 93 | &#button-upload-folder 94 | &#button-bulk-download 95 | &#button-bulk-move 96 | display none 97 | 98 | &.visible 99 | display none 100 | 101 | &#share-state 102 | display inline-block 103 | 104 | #bulk-actions-btngroup a 105 | margin 0 1px 106 | 107 | @media (max-width: 1000px) 108 | .date-column-cell 109 | .size-column-cell 110 | display: none 111 | 112 | #folder-state 113 | .text 114 | display: none 115 | 116 | .itemRow 117 | .tags 118 | display: none 119 | margin-top: 5px 120 | 121 | div#affixbar 122 | border-bottom 1px solid #d5d5d5 123 | 124 | header 125 | padding 0.6em 0 126 | 127 | 128 | @media (max-width: 1200px) 129 | // Portrait tablet to landscape and desktop 130 | 131 | .type-column-cell 132 | display: none 133 | 134 | .application 135 | .container 136 | margin: 0 137 | width: 100% 138 | max-width: 100% 139 | 140 | // Fix bootstrap 141 | @media (max-width: 768px) 142 | .container 143 | max-width: 100vw 144 | -------------------------------------------------------------------------------- /client/app/views/styles/_states.styl: -------------------------------------------------------------------------------- 1 | [data-state~="default"], 2 | [data-state~="enabled"] 3 | cursor inherit 4 | opacity 1 5 | transition opacity 0.2s linear 6 | 7 | [data-state~="disabled"], 8 | cursor not-allowed 9 | opacity 0.5 10 | transition opacity 0.2s linear 11 | 12 | [data-state~="hidden"] 13 | display none 14 | 15 | [data-state~="visible"] 16 | visibility visible 17 | 18 | [data-state~="invisible"] 19 | visibility hidden 20 | 21 | [data-state~="waiting"], 22 | [data-state~="loading"] 23 | cursor wait 24 | 25 | [data-state~="success"], 26 | [data-state~="valid"] 27 | border-color #82cf82 28 | color #82cf82 29 | transition color 0.2s linear, border-color 0.2s linear 30 | 31 | [data-state~="error"], 32 | [data-state~="invalid"] 33 | border-color #f0dddd 34 | color #f0dddd 35 | transition color 0.2s linear, border-color 0.2s linear 36 | -------------------------------------------------------------------------------- /client/app/views/styles/application.styl: -------------------------------------------------------------------------------- 1 | @import 'nib' 2 | @import '_colors' 3 | @import '_cozy' 4 | @import '_icons' 5 | @import '_main' 6 | @import '_responsive' 7 | @import '_list' 8 | 9 | .container 10 | .row 11 | #content 12 | #files 13 | height 100% 14 | 15 | #table-items-body 16 | overflow auto 17 | -------------------------------------------------------------------------------- /client/app/views/templates/breadcrumbs_element.jade: -------------------------------------------------------------------------------- 1 | if model.id == "root" 2 | li 3 | a(href="#") 4 | span 5 | | Files 6 | else 7 | if model.type == "search" 8 | li 9 | a(href="#search/#{model.id}") 10 | | #{model.name} 11 | else 12 | li 13 | a(href="#folders/#{model.id}") 14 | | #{model.name} 15 | -------------------------------------------------------------------------------- /client/app/views/templates/file.jade: -------------------------------------------------------------------------------- 1 | div.extensible-column(role="gridcell") 2 | 3 | block file-path 4 | //- displayed only on search mode 5 | a.file-path(style="display:none") 6 | 7 | div.caption-wrapper 8 | 9 | block title 10 | 11 | .caption(data-file-url="#{attachmentUrl}") 12 | a.link-wrapper.btn-link 13 | div.spinholder(style="display: none") 14 | img(src="images/spinner.svg") 15 | //- type-folder or type-file or type-thumb or type-image or ... 16 | i.icon-type 17 | img.thumb 18 | span.fa.fa-globe 19 | //- the name of the file 20 | span.file-name 21 | 22 | 23 | 24 | block tags 25 | ul.tags 26 | 27 | block empty 28 | .block-empty 29 | 30 | block actions 31 | .operations 32 | a.file-tags(title="#{t('tooltip tag')}") 33 | span.fa.fa-tag 34 | a.file-share(title="#{t('tooltip share')}") 35 | span.fa.fa-share-alt 36 | a.file-edit(title="#{t('tooltip edit')}") 37 | span.fa.fa-pencil-square-o 38 | a.file-download( 39 | href="#{downloadUrl}", 40 | target="_blank", title="#{t('tooltip download')}") 41 | span.fa.fa-download 42 | 43 | div.size-column-cell(role="gridcell") 44 | 45 | div.type-column-cell(role="gridcell") 46 | 47 | div.date-column-cell(role="gridcell") 48 | -------------------------------------------------------------------------------- /client/app/views/templates/file_edit.jade: -------------------------------------------------------------------------------- 1 | extends file 2 | 3 | block title 4 | 5 | .caption 6 | a.link-wrapper.btn-link 7 | 8 | div.spinholder(style="display: none") 9 | img(src="images/spinner.svg") 10 | 11 | i(class="#{iconClass}") 12 | img.thumb(src="#{thumbSrc}") 13 | span.fa.fa-globe 14 | 15 | input.caption.file-edit-name(value=model.name) 16 | 17 | a.btn.btn-sm.btn-cozy.file-edit-save 18 | | #{t("file edit save")} 19 | 20 | a.btn.btn-sm.btn-link.file-edit-cancel 21 | | #{t("file edit cancel")} 22 | 23 | block actions 24 | // empty! 25 | -------------------------------------------------------------------------------- /client/app/views/templates/files.jade: -------------------------------------------------------------------------------- 1 | // List's header. 2 | div(role="rowheader") 3 | div.extensible-column(role="columnheader") 4 | button#select-all 5 | if numSelectedElements == 0 || numElements == 0 6 | i.fa.fa-square-o 7 | else if numSelectedElements == numElements 8 | i.fa.fa-check-square-o 9 | else 10 | i.fa.fa-minus-square-o 11 | 12 | a#down-name.btn.unactive.fa.fa-chevron-down 13 | = t('name') 14 | a#up-name.btn.unactive.fa.fa-chevron-up 15 | = t('name') 16 | 17 | div.size-column-cell(role="columnheader") 18 | a#down-size.btn.unactive.fa.fa-chevron-down 19 | = t('size') 20 | a#up-size.btn.unactive.fa.fa-chevron-up 21 | = t('size') 22 | 23 | div.type-column-cell(role="columnheader") 24 | a#down-class.btn.unactive.fa.fa-chevron-down 25 | = t('type') 26 | a#up-class.btn.unactive.fa.fa-chevron-up 27 | = t('type') 28 | 29 | div.date-column-cell(role="columnheader") 30 | a#down-lastModification.btn.unactive.fa.fa-chevron-down 31 | = t('date') 32 | a#up-lastModification.btn.unactive.fa.fa-chevron-up 33 | = t('date') 34 | 35 | // List of elements. 36 | ul#table-items-body(role="grid") 37 | 38 | // the file info display (popover) 39 | div.file-info 40 | 41 | 42 | // Footer. 43 | footer#file-amount-indicator 44 | footer#no-files-indicator 45 | 46 | if model.type == 'search' 47 | | #{t('no file in search')} 48 | else 49 | | #{t('no file in folder')} 50 | 51 | -------------------------------------------------------------------------------- /client/app/views/templates/modal.jade: -------------------------------------------------------------------------------- 1 | .modal-dialog 2 | .modal-content 3 | .modal-header 4 | button.close(type='button', data-dismiss='modal', aria-hidden='true') × 5 | h4.modal-title #{title} 6 | .modal-body 7 | p 8 | | #{msg} 9 | .modal-footer 10 | if no 11 | button.btn.btn-link#modal-dialog-no(type='button') #{no} 12 | if yes 13 | button.btn.btn-cozy#modal-dialog-yes(type='button') #{yes} -------------------------------------------------------------------------------- /client/app/views/templates/modal_folder.jade: -------------------------------------------------------------------------------- 1 | .modal-dialog 2 | .modal-content 3 | .modal-header 4 | button.close(type='button', data-dismiss='modal', aria-hidden='true') × 5 | h4.modal-title #{t("new folder caption")} 6 | .modal-body 7 | fieldset 8 | .form-group 9 | label(for='inputName') 10 | | #{t("new folder msg")} 11 | input#inputName.form-control(type="text") 12 | .form-group.hide#folder-upload-form 13 | br 14 | p.text-center= t('upload folder separator') 15 | label(for='inputName') 16 | | #{t("upload folder msg")} 17 | input#folder-uploader.form-control(type="file", directory, mozdirectory, webkitdirectory) 18 | .modal-footer 19 | button.btn.btn-link#modal-dialog-no(type='button', data-dismiss='modal') #{t("new folder close")} 20 | button.btn.btn-cozy-contrast#modal-dialog-yes(type='button', disabled="disabled") #{t("new folder send")} 21 | -------------------------------------------------------------------------------- /client/app/views/templates/progressbar.jade: -------------------------------------------------------------------------------- 1 | .progress.active 2 | .progress-bar.progress-bar-info(role="progressbar",aria-valuenow="#{value}",aria-valuemin="0",aria-valuemax="100",style="width: #{value}%") #{value}% 3 | -------------------------------------------------------------------------------- /client/app/views/templates/upload_status.jade: -------------------------------------------------------------------------------- 1 | span= t('upload running') 2 | 3 | #dismiss.btn.btn-cozy.pull-right= t('ok') 4 | .progress.active.pull-right 5 | .progress-bar.progress-bar-info( 6 | style="width: " + value) 7 | .progress-bar.progress-bar-content #{t('total progress')} #{value} 8 | -------------------------------------------------------------------------------- /client/app/widgets/progressbar.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../lib/base_view' 2 | 3 | module.exports = class ProgressbarView extends BaseView 4 | 5 | className: 'progressview' 6 | template: require '../views/templates/progressbar' 7 | value: 0 8 | 9 | initialize: -> 10 | @listenTo @model, 'progress', @update 11 | @listenTo @model, 'sync', @destroy 12 | 13 | @value = @getProgression @model.loaded, @model.total 14 | 15 | update: (e) -> 16 | @value = @getProgression e.loaded, e.total 17 | @$('.progress-bar').text(@value + '%').width @value + '%' 18 | 19 | getProgression: (loaded, total) -> 20 | return parseInt(loaded / total * 100) 21 | 22 | getRenderData: -> 23 | value: @value 24 | name: @model.get 'name' 25 | -------------------------------------------------------------------------------- /client/config.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | glob = require 'glob' 3 | 4 | exports.config = 5 | 6 | files: 7 | javascripts: 8 | joinTo: 9 | 'javascripts/app.js': /^app/ 10 | 'javascripts/vendor.js': /^vendor/ 11 | order: 12 | # Files in `vendor` directories are compiled before other files 13 | # even if they aren't specified in order. 14 | before: [ 15 | 'vendor/scripts/jquery-1.9.1.js' 16 | 'vendor/scripts/underscore-1.4.4.js' 17 | 'vendor/scripts/backbone-1.1.2.js' 18 | 'vendor/scripts/spin.js' 19 | ].concat(glob.sync 'vendor/plugins/**/*.js') 20 | 21 | stylesheets: 22 | joinTo: 'stylesheets/app.css' 23 | order: 24 | before: ['vendor/styles/normalize.css'] 25 | after: ['vendor/styles/helpers.css'] 26 | .concat(glob.sync 'vendor/plugins/**/*.css') 27 | 28 | templates: 29 | defaultExtension: 'jade' 30 | joinTo: 'javascripts/app.js' 31 | 32 | plugins: 33 | jade: 34 | globals: ['t', 'moment', 'filesize'] 35 | 36 | cleancss: 37 | keepSpecialComments: 0 38 | removeEmpty: true 39 | 40 | digest: 41 | referenceFiles: /\.jade$/ 42 | 43 | overrides: 44 | production: 45 | # re-enable when uglifyjs will handle properly in source maps 46 | # with sourcesContent attribute 47 | #optimize: true 48 | sourceMaps: true 49 | paths: 50 | public: path.resolve __dirname, '../build/client/public' 51 | -------------------------------------------------------------------------------- /client/generators/collection/collection.coffee.hbs: -------------------------------------------------------------------------------- 1 | module.exports = class {{#camelize}}{{pluralName}}{{/camelize}}Collection extends Backbone.Collection 2 | model: require '../models/{{name}}' 3 | url: '{{name}}s/' 4 | -------------------------------------------------------------------------------- /client/generators/collection/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [{ 3 | "from": "collection.coffee.hbs", 4 | "to": "app/collections/{{pluralName}}.coffee" 5 | }], 6 | "dependencies": [{ 7 | "name": "collection_test", 8 | "params": "{{pluralName}}" 9 | }] 10 | } 11 | -------------------------------------------------------------------------------- /client/generators/collection_test/collection_test.coffee.hbs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/generators/collection_test/collection_test.coffee.hbs -------------------------------------------------------------------------------- /client/generators/collection_test/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [{ 3 | "from": "collection_test.coffee.hbs", 4 | "to": "test/collections/{{pluralName}}_test.coffee" 5 | }], 6 | "dependencies": [] 7 | } 8 | -------------------------------------------------------------------------------- /client/generators/generator/generated_file.coffee.hbs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/client/generators/generator/generated_file.coffee.hbs -------------------------------------------------------------------------------- /client/generators/generator/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | { 4 | "from": "generator.json.hbs", 5 | "to": "generators/{{name}}/generator.json" 6 | }, 7 | { 8 | "from": "generated_file.coffee.hbs", 9 | "to": "generators/{{name}}/{{name}}.coffee.hbs" 10 | } 11 | ], 12 | "dependencies": [] 13 | } 14 | -------------------------------------------------------------------------------- /client/generators/generator/generator.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "files": [{ 3 | "from": "{{name}}.coffee.hbs", 4 | "to": "app/file.coffee" 5 | }], 6 | "dependencies": [] 7 | } 8 | -------------------------------------------------------------------------------- /client/generators/model/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [{ 3 | "from": "model.coffee.hbs", 4 | "to": "app/models/{{name}}.coffee" 5 | }], 6 | "dependencies": [{ 7 | "name": "model_test", 8 | "params": "{{name}}" 9 | }] 10 | } 11 | -------------------------------------------------------------------------------- /client/generators/model/model.coffee.hbs: -------------------------------------------------------------------------------- 1 | module.exports = class {{#camelize}}{{name}}{{/camelize}}Model extends Backbone.Model 2 | rootUrl: "{{name}}s/" -------------------------------------------------------------------------------- /client/generators/model_test/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [{ 3 | "from": "model_test.coffee.hbs", 4 | "to": "test/models/{{name}}.coffee" 5 | }], 6 | "dependencies": [] 7 | } 8 | -------------------------------------------------------------------------------- /client/generators/model_test/model_test.coffee.hbs: -------------------------------------------------------------------------------- 1 | {{#camelize}}{{name}}{{/camelize}}Model = require 'models/{{name}}' 2 | 3 | describe '{{#camelize}}{{name}}{{/camelize}}Model', -> 4 | beforeEach -> 5 | @model = new {{#camelize}}{{name}}{{/camelize}}Model() 6 | -------------------------------------------------------------------------------- /client/generators/module/collection.coffee.hbs: -------------------------------------------------------------------------------- 1 | module.exports = class {{#camelize}}{{pluralName}}{{/camelize}}Collection extends Backbone.Collection 2 | model: require '../models/{{name}}' 3 | url: '{{name}}s/' 4 | -------------------------------------------------------------------------------- /client/generators/module/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | { 4 | "from": "model.coffee.hbs", 5 | "to": "app/models/{{name}}.coffee" 6 | }, 7 | { 8 | "from": "view.coffee.hbs", 9 | "to": "app/views/{{name}}.coffee" 10 | }, 11 | { 12 | "from": "collection.coffee.hbs", 13 | "to": "app/collections/{{name}}s.coffee" 14 | }, 15 | { 16 | "from": "template.jade.hbs", 17 | "to": "app/views/templates/{{name}}.jade" 18 | }, 19 | { 20 | "from": "style.styl.hbs", 21 | "to": "app/views/styles/_{{name}}.styl" 22 | }, 23 | { 24 | "from": "view_collection.coffee.hbs", 25 | "to": "app/views/{{name}}s.coffee" 26 | } 27 | ], 28 | "dependencies": [] 29 | } 30 | -------------------------------------------------------------------------------- /client/generators/module/model.coffee.hbs: -------------------------------------------------------------------------------- 1 | module.exports = class {{#camelize}}{{name}}{{/camelize}}Model extends Backbone.Model 2 | rootUrl: "{{name}}s/" -------------------------------------------------------------------------------- /client/generators/module/style.styl.hbs: -------------------------------------------------------------------------------- 1 | .{{name}} 2 | // {{#camelize}}{{name}}{{/camelize}} Module 3 | 4 | -------------------------------------------------------------------------------- /client/generators/module/template.jade.hbs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/generators/module/view.coffee.hbs: -------------------------------------------------------------------------------- 1 | BaseView = require '../lib/base_view' 2 | 3 | module.exports = class {{#camelize}}{{name}}{{/camelize}}View extends BaseView 4 | template: require './templates/{{name}}' 5 | className: '{{name}}' 6 | -------------------------------------------------------------------------------- /client/generators/module/view_collection.coffee.hbs: -------------------------------------------------------------------------------- 1 | ViewCollection = require '../lib/view_collection' 2 | {{#camelize}}{{name}}{{/camelize}}sCollection = require '../collections/{{name}}s' 3 | {{#camelize}}{{name}}{{/camelize}}View = require './{{name}}' 4 | 5 | module.exports = class {{#camelize}}{{name}}{{/camelize}}sView extends ViewCollection 6 | el: '#{{name}}s' 7 | 8 | collection: new {{#camelize}}{{name}}{{/camelize}}sCollection() 9 | itemview: {{#camelize}}{{name}}{{/camelize}}View 10 | 11 | afterRender: -> 12 | @collection.on 'reset', => 13 | @renderAll() 14 | @collection.on 'add', (model) => 15 | @renderOne model 16 | -------------------------------------------------------------------------------- /client/generators/style/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [{ 3 | "from": "style.styl.hbs", 4 | "to": "app/views/styles/_{{name}}.styl" 5 | }], 6 | "dependencies": [] 7 | } 8 | -------------------------------------------------------------------------------- /client/generators/style/style.styl.hbs: -------------------------------------------------------------------------------- 1 | .{{name}} 2 | // {{#camelize}}{{name}}{{/camelize}} Module 3 | -------------------------------------------------------------------------------- /client/generators/template/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [{ 3 | "from": "template.hbs.hbs", 4 | "to": "app/views/templates/{{name}}.jade" 5 | }], 6 | "dependencies": [] 7 | } 8 | -------------------------------------------------------------------------------- /client/generators/template/template.hbs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /client/generators/view/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [{ 3 | "from": "view.coffee.hbs", 4 | "to": "app/views/{{name}}.coffee" 5 | }], 6 | "dependencies": [{ 7 | "name": "view_test", 8 | "params": "{{name}}" 9 | }] 10 | } 11 | -------------------------------------------------------------------------------- /client/generators/view/view.coffee.hbs: -------------------------------------------------------------------------------- 1 | BaseView = require '../lib/base_view' 2 | 3 | module.exports = class {{#camelize}}{{name}}{{/camelize}}View extends BaseView 4 | template: require './templates/{{name}}' 5 | className: '{{name}}' 6 | -------------------------------------------------------------------------------- /client/generators/view_test/generator.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | { 4 | "from": "view_test.coffee.hbs", 5 | "to": "test/views/{{name}}.coffee" 6 | } 7 | ], 8 | "dependencies": [] 9 | } 10 | -------------------------------------------------------------------------------- /client/generators/view_test/view_test.coffee.hbs: -------------------------------------------------------------------------------- 1 | {{#camelize}}{{name}}{{/camelize}}View = require 'views/{{name}}' 2 | 3 | describe '{{#camelize}}{{name}}{{/camelize}}View', -> 4 | beforeEach -> 5 | @view = new {{#camelize}}{{name}}{{/camelize}}View() 6 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "clean-css-brunch": "1.7.2", 4 | "coffee-script-brunch": "1.8.2", 5 | "css-brunch": "1.7.0", 6 | "digest-brunch": "1.5.1", 7 | "glob": "^5.0.10", 8 | "jade-brunch": "1.8.1", 9 | "javascript-brunch": "1.7.1", 10 | "json-brunch": "^1.5.4", 11 | "stylus-brunch": "1.8.1" 12 | }, 13 | "devDependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /client/tests/e2e/upload.coffee: -------------------------------------------------------------------------------- 1 | __utils__ = require('clientutils').create() 2 | 3 | helpers = require('../helpers')(casper, __utils__) 4 | 5 | casper.options.viewportSize = width: 1280, height: 800 6 | 7 | uploadButton = '#button-upload-new-file' 8 | 9 | casper.test.begin 'Upload - upload one file', (test) -> 10 | 11 | casper.start 'http://localhost:9121', -> 12 | 13 | test.assertTitle 'Cozy - Files', 'Checks that application is properly started' 14 | test.assertVisible uploadButton, 'Upload button should be visible' 15 | 16 | casper.run -> test.done() 17 | 18 | # To be implemented 19 | casper.test.begin 'Upload - upload multiple files', (test) -> 20 | test.done() 21 | casper.test.begin 'Upload - upload one folder', (test) -> 22 | test.done() 23 | casper.test.begin 'Upload - upload files through drag an drop', (test) -> 24 | test.done() 25 | -------------------------------------------------------------------------------- /client/tests/helpers.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (casper, utils) -> helpers = 2 | 3 | _test: null 4 | 5 | assertHasClass: (selector, className) -> 6 | classNames = helpers.getClassNames selector 7 | helpers._test.assert( 8 | className in classNames, 9 | "#{selector} has class #{className}" 10 | ) 11 | 12 | assertHasntClass: (selector, className) -> 13 | classNames = casper.getElementAttribute selector, 'className' 14 | helpers._test.assert( 15 | className not in classNames, 16 | "#{selector} hasn't class #{className}" 17 | ) 18 | 19 | getClassNames: (selector) -> 20 | classNames = casper.getElementInfo(selector).attributes.class 21 | return classNames.split ' ' 22 | 23 | getElementIndexByName: (elementName) -> 24 | elementIndex = casper.evaluate (elementName) -> 25 | result = null 26 | selector = 'tr.folder-row' 27 | Array::forEach.call $(selector), (element, index) -> 28 | elementText = $(element).find('a.btn-link span').html() 29 | result = index if elementText is elementName 30 | 31 | return result 32 | , elementName 33 | 34 | return elementIndex 35 | 36 | getElementSelectorByName: (elementName) -> 37 | index = helpers.getElementIndexByName elementName 38 | selector = "tr.folder-row:nth-of-type(#{index + 1})" 39 | helpers._test.assertExist selector, "#{selector} should exist" 40 | return selector 41 | 42 | navigateToFolder: (folderName) -> 43 | casper.click "#{helpers.getElementSelectorByName folderName} a.btn-link" 44 | casper.waitForUrl /#folders\/[a-z0-9]+/ 45 | casper.waitForSelector 'tr.folder-row' 46 | casper.wait 2500 47 | 48 | openShareModalByName: (elementName) -> 49 | selector = helpers.getElementSelectorByName elementName 50 | 51 | casper.thenClick "#{selector} .file-share" 52 | casper.waitUntilVisible '#cozy-clearance-modal' 53 | 54 | # the modal has an animation 55 | casper.wait 500 56 | 57 | makeAccessPrivate: (elementName) -> 58 | helpers.openShareModalByName elementName 59 | 60 | # forces the access to private/limited 61 | casper.thenClick '#share-private' 62 | 63 | saveAndCloseModal: -> 64 | casper.thenClick '#modal-dialog-yes' 65 | casper.waitWhileVisible '#cozy-clearance-modal' 66 | # if there is a request, we wait for it to end 67 | casper.wait 500 68 | 69 | makeAccessPublic: (elementName) -> 70 | helpers.openShareModalByName elementName 71 | 72 | casper.thenClick '#share-public' 73 | casper.waitUntilVisible '#public-url' 74 | 75 | # removes all limited access 76 | # casper.thenClick 'li.clearance-line i.icon-remove' 77 | -------------------------------------------------------------------------------- /client/vendor/plugins/gallery/init.js: -------------------------------------------------------------------------------- 1 | //jshint browser: true, strict: false 2 | // 3 | // Allow to display a slideshow of every images in the folder 4 | // 5 | if (typeof window.plugins !== "object") { 6 | window.plugins = {}; 7 | } 8 | window.plugins.gallery = { 9 | name: "Gallery", 10 | active: true, 11 | extensions: ['jpg', 'jpeg', 'png', 'gif'], 12 | getFiles: function (node) { 13 | return window.plugins.helpers.getFiles(this.extensions, node); 14 | }, 15 | addGallery: function (params) { 16 | var images, gal; 17 | images = this.getFiles(); 18 | // don't display the gallery in folders with more than 50 pictures, 19 | // as the gallery library may make the browser unresponsive 20 | if (images.length > 0 && images.length < 50) { 21 | gal = document.getElementById('gallery'); 22 | if (gal === null) { 23 | gal = document.createElement('div'); 24 | gal.id = "gallery"; 25 | gal.style.display = "none"; 26 | document.body.appendChild(gal); 27 | } else { 28 | gal.innerHTML = ''; 29 | } 30 | Array.prototype.forEach.call(images, function (elmt, idx) { 31 | var a, img; 32 | a = document.createElement('a'); 33 | a.href = elmt.dataset.fileUrl; 34 | img = document.createElement('img'); 35 | a.appendChild(img); 36 | gal.appendChild(a); 37 | window.plugins.helpers.addIcon(elmt, function () { 38 | var event = document.createEvent("MouseEvent"); 39 | event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); 40 | a.dispatchEvent(event); 41 | }); 42 | }); 43 | window.baguetteBox.run('#gallery', { 44 | captions: true, // true|false - Display image captions 45 | buttons: 'auto', // 'auto'|true|false - Display buttons 46 | async: false, // true|false - Load files asynchronously 47 | preload: 2, // [number] - How many files should be preloaded from current image 48 | animation: 'slideIn' // 'slideIn'|'fadeIn'|false - Animation type 49 | }); 50 | } 51 | }, 52 | onAdd: { 53 | /** 54 | * Should return true if plugin applies on added subtree 55 | * 56 | * @param {DOMNode} root node of added subtree 57 | */ 58 | condition: function (node) { 59 | return this.getFiles(node).length > 0; 60 | }, 61 | /** 62 | * Perform action on added subtree 63 | * 64 | * @param {DOMNode} root node of added subtree 65 | */ 66 | action: function (node) { 67 | this.addGallery(); 68 | } 69 | }, 70 | onDelete: { 71 | condition: function (node) { 72 | return this.getImages(node).length > 0; 73 | }, 74 | action: function (node) { 75 | this.addGallery(); 76 | } 77 | }, 78 | /** 79 | * Called when plugin is activated 80 | */ 81 | onActivate: function () { 82 | }, 83 | /** 84 | * Called when plugin is deactivated 85 | */ 86 | onDeactivate: function () { 87 | }, 88 | listeners: { 89 | 'load': function (params) { 90 | this.addGallery(); 91 | } 92 | } 93 | }; 94 | -------------------------------------------------------------------------------- /client/vendor/plugins/markdown/init.js: -------------------------------------------------------------------------------- 1 | //jshint browser: true, strict: false 2 | // 3 | // Allow to preview markdown files 4 | // 5 | if (typeof window.plugins !== "object") { 6 | window.plugins = {}; 7 | } 8 | window.plugins.markdown = { 9 | name: "Markdown", 10 | active: true, 11 | extensions: ['md', 'markdown'], 12 | getFiles: function (node) { 13 | return window.plugins.helpers.getFiles(this.extensions, node); 14 | }, 15 | addGallery: function (params) { 16 | var files; 17 | files = this.getFiles(); 18 | if (files.length > 0) { 19 | Array.prototype.forEach.call(files, function (elmt, idx) { 20 | window.plugins.helpers.addIcon(elmt, function () { 21 | var xhr = new XMLHttpRequest(); 22 | xhr.onreadystatechange = function () { 23 | if (xhr.readyState === 4) { 24 | window.plugins.helpers.modal({body: window.markdown.toHTML(xhr.responseText), size: 'large'}); 25 | } 26 | }; 27 | xhr.open('GET', elmt.dataset.fileUrl, true); 28 | xhr.send(null); 29 | }); 30 | }); 31 | } 32 | }, 33 | onAdd: { 34 | condition: function (node) { 35 | return this.getFiles(node).length > 0; 36 | }, 37 | action: function (node) { 38 | this.addGallery(); 39 | } 40 | }, 41 | onDelete: { 42 | condition: function (node) { 43 | return this.getFiles(node).length > 0; 44 | }, 45 | action: function (node) { 46 | this.addGallery(); 47 | } 48 | }, 49 | listeners: { 50 | 'load': function (params) { 51 | this.addGallery(); 52 | } 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /client/vendor/plugins/medias/init.js: -------------------------------------------------------------------------------- 1 | //jshint browser: true, strict: false 2 | // 3 | // Allow to preview medias files 4 | // 5 | if (typeof window.plugins !== "object") { 6 | window.plugins = {}; 7 | } 8 | window.plugins.medias = { 9 | name: "Medias", 10 | active: true, 11 | getFiles: function (node) { 12 | return document.querySelectorAll('[data-file-type="music"], [data-file-type="video"]') 13 | }, 14 | addGallery: function (params) { 15 | var files; 16 | files = this.getFiles(); 17 | if (files.length > 0) { 18 | Array.prototype.forEach.call(files, function (elmt, idx) { 19 | window.plugins.helpers.addIcon(elmt, function () { 20 | switch (elmt.dataset.fileType) { 21 | case 'music': 22 | window.plugins.helpers.modal({body: '
', size: 'large'}); 23 | break; 24 | case 'video': 25 | window.plugins.helpers.modal({body: '
', size: 'large'}); 26 | break; 27 | } 28 | }); 29 | }); 30 | } 31 | }, 32 | onAdd: { 33 | condition: function (node) { 34 | return this.getFiles(node).length > 0; 35 | }, 36 | action: function (node) { 37 | this.addGallery(); 38 | } 39 | }, 40 | onDelete: { 41 | condition: function (node) { 42 | return this.getFiles(node).length > 0; 43 | }, 44 | action: function (node) { 45 | this.addGallery(); 46 | } 47 | }, 48 | listeners: { 49 | 'load': function (params) { 50 | this.addGallery(); 51 | } 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /client/vendor/plugins/viewer/init.js: -------------------------------------------------------------------------------- 1 | //jshint browser: true, strict: false, maxstatements: false 2 | // 3 | // Use Viewer.js to preview PDF and OpenDocument files 4 | // 5 | if (typeof window.plugins !== "object") { 6 | window.plugins = {}; 7 | } 8 | window.plugins.viewer = { 9 | name: "Viewer", 10 | active: true, 11 | extensions: ['pdf', 'ods', 'odt'], 12 | getFiles: function (node) { 13 | return window.plugins.helpers.getFiles(this.extensions, node); 14 | }, 15 | addGallery: function (params) { 16 | var files; 17 | files = this.getFiles(); 18 | if (files.length > 0) { 19 | Array.prototype.forEach.call(files, function (elmt, idx) { 20 | window.plugins.helpers.addIcon(elmt, function () { 21 | var viewer; 22 | viewer = document.createElement('iframe'); 23 | viewer.id = 'viewer'; 24 | viewer.setAttribute('src', 'ViewerJS/#../' + elmt.dataset.fileUrl); 25 | viewer.setAttribute('height', window.innerHeight * 0.7); 26 | viewer.setAttribute('width', '100%'); 27 | viewer.setAttribute('allowfullscreen', 'allowfullscreen'); 28 | viewer.setAttribute('webkitallowfullscreen', true); 29 | window.plugins.helpers.modal({body: viewer.outerHTML, size: 'large'}); 30 | }); 31 | }); 32 | } 33 | }, 34 | onAdd: { 35 | condition: function (node) { 36 | return this.getFiles(node).length > 0; 37 | }, 38 | action: function (node) { 39 | this.addGallery(); 40 | } 41 | }, 42 | onDelete: { 43 | condition: function (node) { 44 | return this.getFiles(node).length > 0; 45 | }, 46 | action: function (node) { 47 | this.addGallery(); 48 | } 49 | }, 50 | listeners: { 51 | 'load': function (params) { 52 | this.addGallery(); 53 | } 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /client/vendor/scripts/filesize.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | 2013 Jason Mulligan 3 | @license BSD-3 4 | @link http://filesizejs.com 5 | @module filesize 6 | @version 2.0.0 7 | */ 8 | (function(r){function f(f,c){var b="",g=!1,l=6,a,h,q,d,m,n,p,e,k;if(isNaN(f))throw Error("Invalid arguments");c=c||{};h=!0===c.bits;e=!0===c.unix;a=void 0!==c.base?c.base:e?2:10;m=void 0!==c.round?c.round:e?1:2;k=void 0!==c.spacer?c.spacer:e?"":" ";d=Number(f);(q=0>d)&&(d=-d);if(0===d)b=e?"0":"0"+k+"B";else for(p=s[a][h?"bits":"bytes"];l--;)if(n=p[l][1],a=p[l][0],d>=n){t.test(a)&&(g=!0,m=0);b=(d/n).toFixed(m);!g&&e?(h&&u.test(a)&&(a=a.toLowerCase()),a=a.charAt(0),g=v.exec(b),h||"k"!==a||(a="K"),null!== 9 | g&&void 0!==g[1]&&w.test(g[1])&&(b=parseInt(b,x)),b+=k+a):e||(b+=k+a);break}q&&(b="-"+b);return b}var u=/b$/,t=/^B$/,x=10,v=/\.(.*)/,w=/^0$/,s={2:{bits:[["B",1],["kb",128],["Mb",131072],["Gb",134217728],["Tb",137438953472],["Pb",0x800000000000]],bytes:[["B",1],["kB",1024],["MB",1048576],["GB",1073741824],["TB",1099511627776],["PB",0x4000000000000]]},10:{bits:[["B",1],["kb",125],["Mb",125E3],["Gb",125E6],["Tb",125E9],["Pb",125E12]],bytes:[["B",1],["kB",1E3],["MB",1E6],["GB",1E9],["TB",1E12],["PB", 10 | 1E15]]}};"undefined"!==typeof exports?module.exports=f:"function"===typeof define?define(function(){return f}):r.filesize=f})(this); 11 | //# sourceMappingURL=filesize.map 12 | -------------------------------------------------------------------------------- /client/vendor/scripts/jquery.spin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2011-2014 Felix Gnass 3 | * Licensed under the MIT license 4 | */ 5 | 6 | /* 7 | 8 | Basic Usage: 9 | ============ 10 | 11 | $('#el').spin(); // Creates a default Spinner using the text color of #el. 12 | $('#el').spin({ ... }); // Creates a Spinner using the provided options. 13 | 14 | $('#el').spin(false); // Stops and removes the spinner. 15 | 16 | Using Presets: 17 | ============== 18 | 19 | $('#el').spin('small'); // Creates a 'small' Spinner using the text color of #el. 20 | $('#el').spin('large', '#fff'); // Creates a 'large' white Spinner. 21 | 22 | Adding a custom preset: 23 | ======================= 24 | 25 | $.fn.spin.presets.flower = { 26 | lines: 9 27 | length: 10 28 | width: 20 29 | radius: 0 30 | } 31 | 32 | $('#el').spin('flower', 'red'); 33 | 34 | */ 35 | 36 | $.fn.spin = function(opts, color) { 37 | return this.each(function() { 38 | var $this = $(this), 39 | data = $this.data(); 40 | 41 | if (data.spinner) { 42 | data.spinner.stop(); 43 | delete data.spinner; 44 | } 45 | if (opts !== false) { 46 | opts = $.extend( 47 | { color: color || $this.css('color') }, 48 | $.fn.spin.presets[opts] || opts 49 | ) 50 | // console.log(opts); 51 | data.spinner = new Spinner(opts).spin(this) 52 | // console.log(data.spinner); 53 | } 54 | }); 55 | } 56 | 57 | $.fn.spin.presets = { 58 | tiny: { lines: 8, length: 2, width: 2, radius: 3 }, 59 | small: { lines: 8, length: 4, width: 3, radius: 5 }, 60 | large: { lines: 10, length: 8, width: 4, radius: 8 } 61 | } 62 | -------------------------------------------------------------------------------- /client/vendor/styles/gradient.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: rgb(255,255,255); 3 | background: -moz-linear-gradient(top, rgba(229,229,229,1) 0%, rgba(255,255,255,1) 100%); 4 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(229,229,229,1)), color-stop(100%,rgba(255,255,255,1))); 5 | background: -webkit-linear-gradient(top, rgba(229,229,229,1) 0%,rgba(255,255,255,1) 100%); 6 | background: -o-linear-gradient(top, rgba(229,229,229,1) 0%,rgba(255,255,255,1) 100%); 7 | background: -ms-linear-gradient(top, rgba(229,229,229,1) 0%,rgba(255,255,255,1) 100%); 8 | background: linear-gradient(to bottom, rgba(229,229,229,1) 0%,rgba(255,255,255,1) 100%); 9 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e5e5e5', endColorstr='#ffffff',GradientType=0 ); 10 | background-attachment:fixed; 11 | } -------------------------------------------------------------------------------- /client/vendor/styles/helpers.css: -------------------------------------------------------------------------------- 1 | .chromeframe { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0; } 2 | .ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; *line-height: 0; } 3 | .ir br { display: none; } 4 | .hidden { display: none !important; visibility: hidden; } 5 | .visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } 6 | .visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; } 7 | .invisible { visibility: hidden; } 8 | .clearfix:before, .clearfix:after { content: ""; display: table; } 9 | .clearfix:after { clear: both; } 10 | .clearfix { *zoom: 1; } 11 | 12 | @media print { 13 | * { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } 14 | a, a:visited { text-decoration: underline; } 15 | a[href]:after { content: " (" attr(href) ")"; } 16 | abbr[title]:after { content: " (" attr(title) ")"; } 17 | .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } 18 | pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } 19 | thead { display: table-header-group; } 20 | tr, img { page-break-inside: avoid; } 21 | img { max-width: 100% !important; } 22 | @page { margin: 0.5cm; } 23 | p, h2, h3 { orphans: 3; widows: 3; } 24 | h2, h3 { page-break-after: avoid; } 25 | } 26 | -------------------------------------------------------------------------------- /client/vendor/styles/jquery.tagit.css: -------------------------------------------------------------------------------- 1 | ul.tagit { 2 | padding: 1px 5px; 3 | overflow: auto; 4 | margin-left: inherit; /* usually we don't want the regular ul margins. */ 5 | margin-right: inherit; 6 | } 7 | ul.tagit li { 8 | display: block; 9 | float: left; 10 | margin: 2px 5px 2px 0; 11 | } 12 | ul.tagit li.tagit-choice { 13 | position: relative; 14 | line-height: inherit; 15 | } 16 | input.tagit-hidden-field { 17 | display: none; 18 | } 19 | ul.tagit li.tagit-choice-read-only { 20 | padding: .2em .5em .2em .5em; 21 | } 22 | 23 | ul.tagit li.tagit-choice-editable { 24 | padding: .2em 18px .2em .5em; 25 | } 26 | 27 | ul.tagit li.tagit-new { 28 | padding: .25em 4px .25em 0; 29 | } 30 | 31 | ul.tagit li.tagit-choice a.tagit-label { 32 | cursor: pointer; 33 | text-decoration: none; 34 | } 35 | ul.tagit li.tagit-choice .tagit-close { 36 | cursor: pointer; 37 | position: absolute; 38 | right: .1em; 39 | top: 50%; 40 | margin-top: -8px; 41 | line-height: 17px; 42 | } 43 | 44 | /* used for some custom themes that don't need image icons */ 45 | ul.tagit li.tagit-choice .tagit-close .text-icon { 46 | display: none; 47 | } 48 | 49 | ul.tagit li.tagit-choice input { 50 | display: block; 51 | float: left; 52 | margin: 2px 5px 2px 0; 53 | } 54 | ul.tagit input[type="text"] { 55 | -moz-box-sizing: border-box; 56 | -webkit-box-sizing: border-box; 57 | box-sizing: border-box; 58 | 59 | -moz-box-shadow: none; 60 | -webkit-box-shadow: none; 61 | box-shadow: none; 62 | 63 | border: none; 64 | margin: 0; 65 | padding: 0; 66 | width: inherit; 67 | background-color: inherit; 68 | outline: none; 69 | } 70 | -------------------------------------------------------------------------------- /client/vendor/styles/tagit.ui-zendesk.css: -------------------------------------------------------------------------------- 1 | 2 | /* Optional scoped theme for tag-it which mimics the zendesk widget. */ 3 | 4 | 5 | ul.tagit { 6 | border-style: solid; 7 | border-width: 1px; 8 | border-color: #C6C6C6; 9 | background: inherit; 10 | } 11 | ul.tagit li.tagit-choice { 12 | -moz-border-radius: 6px; 13 | border-radius: 6px; 14 | -webkit-border-radius: 6px; 15 | border: 1px solid #CAD8F3; 16 | 17 | background: none; 18 | background-color: #DEE7F8; 19 | 20 | font-weight: normal; 21 | } 22 | ul.tagit li.tagit-choice .tagit-label:not(a) { 23 | color: #555; 24 | } 25 | ul.tagit li.tagit-choice a.tagit-close { 26 | text-decoration: none; 27 | } 28 | ul.tagit li.tagit-choice .tagit-close { 29 | right: .4em; 30 | } 31 | ul.tagit li.tagit-choice .ui-icon { 32 | display: none; 33 | } 34 | ul.tagit li.tagit-choice .tagit-close .text-icon { 35 | display: inline; 36 | font-family: arial, sans-serif; 37 | font-size: 16px; 38 | line-height: 16px; 39 | color: #777; 40 | } 41 | ul.tagit li.tagit-choice:hover, ul.tagit li.tagit-choice.remove { 42 | background-color: #bbcef1; 43 | border-color: #6d95e0; 44 | } 45 | ul.tagit li.tagit-choice a.tagLabel:hover, 46 | ul.tagit li.tagit-choice a.tagit-close .text-icon:hover { 47 | color: #222; 48 | } 49 | ul.tagit input[type="text"] { 50 | color: #333333; 51 | background: none; 52 | } 53 | .ui-widget { 54 | font-size: 1.1em; 55 | } 56 | 57 | /* Forked from a jQuery UI theme, so that we don't require the jQuery UI CSS as a dependency. */ 58 | .tagit-autocomplete.ui-autocomplete { position: absolute; cursor: default; } 59 | * html .tagit-autocomplete.ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ 60 | .tagit-autocomplete.ui-menu { 61 | list-style:none; 62 | padding: 2px; 63 | margin: 0; 64 | display:block; 65 | float: left; 66 | } 67 | .tagit-autocomplete.ui-menu .ui-menu { 68 | margin-top: -3px; 69 | } 70 | .tagit-autocomplete.ui-menu .ui-menu-item { 71 | margin:0; 72 | padding: 0; 73 | zoom: 1; 74 | float: left; 75 | clear: left; 76 | width: 100%; 77 | } 78 | .tagit-autocomplete.ui-menu .ui-menu-item a { 79 | text-decoration:none; 80 | display:block; 81 | padding:.2em .4em; 82 | line-height:1.5; 83 | zoom:1; 84 | } 85 | .tagit-autocomplete .ui-menu .ui-menu-item a.ui-state-hover, 86 | .tagit-autocomplete .ui-menu .ui-menu-item a.ui-state-active { 87 | font-weight: normal; 88 | margin: -1px; 89 | } 90 | .tagit-autocomplete.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff 50% 50% repeat-x; color: #222222; } 91 | .tagit-autocomplete.ui-corner-all, .tagit-autocomplete .ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; -khtml-border-radius: 4px; border-radius: 4px; } 92 | .tagit-autocomplete .ui-state-hover, .tagit-autocomplete .ui-state-focus { border: 1px solid #999999; background: #dadada; font-weight: normal; color: #212121; } 93 | .tagit-autocomplete .ui-state-active { border: 1px solid #aaaaaa; } 94 | 95 | .tagit-autocomplete .ui-widget-content { border: 1px solid #aaaaaa; } 96 | .tagit .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px,1px,1px,1px); } 97 | 98 | 99 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "indentation" : { 3 | "value" : 4, 4 | "level" : "error" 5 | }, 6 | "no_empty_param_list": { 7 | "level": "warn" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cozy-files", 3 | "version": "1.1.26", 4 | "description": "Cozy files allows to store files in your cozy", 5 | "author": "Cozy Cloud (http://cozycloud.cc)", 6 | "license": "AGPL-3.0", 7 | "engines": [ 8 | "node >= 0.10.0" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/cozy/cozy-files.git" 13 | }, 14 | "main": "build/server.js", 15 | "dependencies": { 16 | "americano": "0.4.4", 17 | "archiver": "0.10.1", 18 | "async": "1.5.2", 19 | "axon": "0.6.1", 20 | "cozy-clearance": "0.1.23", 21 | "cozy-notifications-helper": "1.0.2", 22 | "cozy-realtime-adapter": "1.0.1", 23 | "cozydb": "0.1.10", 24 | "jade": "1.11.0", 25 | "mime": "1.2.11", 26 | "moment": "2.10.6", 27 | "multiparty": "3.3.0", 28 | "node-polyglot": "0.3.0", 29 | "printit": "0.1.18", 30 | "request-json": "0.5.5" 31 | }, 32 | "devDependencies": { 33 | "brunch": "1.8.5", 34 | "chai": "1.9.1", 35 | "coffee-script": "latest", 36 | "coffeelint": "1.14.2", 37 | "cozy-fixtures": "1.1.3", 38 | "decompress": "2.1.1", 39 | "form-data": "^0.2.0", 40 | "mocha": "2.1.0", 41 | "nodemon": "1.8.1", 42 | "rimraf": "2.2.8" 43 | }, 44 | "scripts": { 45 | "build": "npm run build:client && npm run build:server", 46 | "build:client": "cd client && brunch b", 47 | "build:server": "cake build", 48 | "lint": "coffeelint -f coffeelint.json --quiet -r server server.coffee", 49 | "test": "npm run test:server", 50 | "test:build": "env USE_JS=true npm run test:server", 51 | "test:server": "env NODE_ENV=test PORT=4444 mocha --reporter spec --compilers coffee:coffee-script/register test/*_test.coffee", 52 | "start": "node build/server.js", 53 | "dev:client": "cd client && brunch w", 54 | "dev:server": "nodemon server.coffee --ignore client/", 55 | "dev": "npm run dev:server & npm run dev:client" 56 | }, 57 | "cozy-permissions": { 58 | "File": { 59 | "description": "Store files in your cozy" 60 | }, 61 | "Folder": { 62 | "description": "Store folders in your cozy" 63 | }, 64 | "Notification": { 65 | "description": "Display a notification when guests change file" 66 | }, 67 | "send mail": { 68 | "description": "Send links by mail" 69 | }, 70 | "CozyInstance": { 71 | "description": "To display the application in the right language, the application requires access to your instance informations." 72 | }, 73 | "Contact": { 74 | "description": "To easily find your contact when sharing files & folders." 75 | }, 76 | "User": { 77 | "description": "Need information for the from part of email sent." 78 | }, 79 | "send mail from user": { 80 | "description": "Send links by mail" 81 | }, 82 | "send mail to user": { 83 | "description": "Send links by mail" 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /server.coffee: -------------------------------------------------------------------------------- 1 | americano = require 'americano' 2 | errorHandler = require './server/middlewares/errors' 3 | initialize = require './server/initialize' 4 | 5 | application = module.exports = (callback) -> 6 | options = 7 | name: 'cozy-files' 8 | root: __dirname 9 | port: process.env.PORT || 9121 10 | host: process.env.HOST || '127.0.0.1' 11 | 12 | initialize.beforeStart -> 13 | americano.start options, (err, app, server) -> 14 | app.server = server 15 | app.use errorHandler 16 | initialize.afterStart app, server, callback 17 | 18 | if not module.parent 19 | application() 20 | -------------------------------------------------------------------------------- /server/config.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | americano = require 'americano' 3 | errorHandler = require './middlewares/errors' 4 | getTemplateExt = require './helpers/get_template_ext' 5 | 6 | staticMiddleware = americano.static path.resolve(__dirname, '../client/public'), 7 | maxAge: 86400000 8 | 9 | publicStatic = (req, res, next) -> 10 | 11 | # Allows assets to be loaded from any route 12 | detectAssets = /\/(stylesheets|javascripts|images|fonts)+\/(.+)$/ 13 | assetsMatched = detectAssets.exec req.url 14 | 15 | if assetsMatched? 16 | req.url = assetsMatched[0] 17 | 18 | staticMiddleware req, res, (err) -> next err 19 | 20 | GB = 1024 * 1024 * 1024 21 | 22 | config = 23 | common: 24 | set: 25 | 'view engine': getTemplateExt() 26 | 'views': path.resolve __dirname, 'views' 27 | 28 | engine: 29 | js: (path, locales, callback) -> 30 | callback null, require(path)(locales) 31 | 32 | use: [ 33 | americano.bodyParser() 34 | staticMiddleware 35 | publicStatic 36 | ] 37 | afterStart: (app, server) -> 38 | # move here necessary after express 4.4 39 | app.use errorHandler 40 | 41 | development: [ 42 | americano.logger 'dev' 43 | ] 44 | production: [ 45 | americano.logger 'short' 46 | ] 47 | plugins: [ 48 | 'cozydb' 49 | ] 50 | 51 | module.exports = config 52 | -------------------------------------------------------------------------------- /server/controllers/index.coffee: -------------------------------------------------------------------------------- 1 | async = require 'async' 2 | cozydb = require 'cozydb' 3 | 4 | module.exports.main = (req, res, next) -> 5 | async.parallel [ 6 | (cb) -> cozydb.api.getCozyLocale cb 7 | (cb) -> cozydb.api.getCozyTags cb 8 | (cb) -> cozydb.api.getCozyInstance cb 9 | ], (err, results) -> 10 | 11 | if err then next err 12 | else 13 | [locale, tags, instance] = results 14 | if instance?.domain? and instance.domain isnt 'domain.not.set' 15 | # Parse domain 16 | domain = instance.domain 17 | if domain.indexOf('https') is -1 18 | domain = "https://#{domain}" 19 | if domain.slice('-1') is "/" 20 | domain = domain.substring 0, domain.length-1 21 | domain = domain + "/public/files/" 22 | else 23 | domain = false 24 | res.render "index", imports: """ 25 | window.locale = "#{locale}"; 26 | window.tags = "#{tags.join(',').replace('\"', '')}".split(','); 27 | window.domain = "#{domain}"; 28 | """ 29 | -------------------------------------------------------------------------------- /server/controllers/sharing.coffee: -------------------------------------------------------------------------------- 1 | File = require '../models/file' 2 | Folder = require '../models/folder' 3 | User = require '../models/user' 4 | helpers = require '../helpers/sharing' 5 | clearance = require 'cozy-clearance' 6 | async = require 'async' 7 | fs = require 'fs' 8 | 9 | localization = require '../lib/localization_manager' 10 | 11 | templatefile = require('path').join __dirname, '../views/sharemail.jade' 12 | mailTemplate = notiftemplate = localization.getEmailTemplate 'sharemail.jade' 13 | 14 | clearanceCtl = clearance.controller 15 | mailTemplate: (options, callback) -> 16 | # Use async to retrieve all wanted informations 17 | User.getUserInfo (err, user) -> 18 | if err? 19 | callback err 20 | else 21 | options.type = options.doc.docType.toLowerCase() 22 | options.displayName = user.name \ 23 | or localization.t 'default user name' 24 | options.displayEmail = user.email 25 | options.localization = localization 26 | options.displayLabel = localization.t "view #{options.type}" 27 | 28 | callback null, mailTemplate options 29 | 30 | 31 | mailSubject: (options, callback) -> 32 | type = options.doc.docType.toLowerCase() 33 | name = options.doc.name 34 | User.getDisplayName (err, displayName) -> 35 | if err? 36 | callback err 37 | else 38 | displayName = displayName or localization.t 'default user name' 39 | callback null, localization.t 'email sharing subject', 40 | displayName: displayName 41 | name: name 42 | 43 | attachments: [ 44 | path: fs.realpathSync './build/client/public/images/cozy-logo.png' 45 | filename: 'cozy-logo.png' 46 | cid: 'cozy-logo' 47 | ] 48 | 49 | 50 | # fetch file or folder, put it in req.doc 51 | module.exports.fetch = (req, res, next, id) -> 52 | async.parallel [ 53 | (cb) -> File.find id, (err, file) -> cb null, file 54 | (cb) -> Folder.find id, (err, folder) -> cb null, folder 55 | ], (err, results) -> 56 | [file, folder] = results 57 | doc = file or folder 58 | if doc 59 | req.doc = doc 60 | next() 61 | else 62 | err = new Error 'bad usage' 63 | err.status = 400 64 | next err 65 | 66 | # retrieve inherited sharing info 67 | module.exports.details = (req, res, next) -> 68 | req.doc.getInheritedClearance (err, inherited) -> 69 | if err? then next err 70 | else 71 | res.send inherited: inherited 72 | 73 | # do not use clearanceCtl, because we handle notifications 74 | module.exports.change = (req, res, next) -> 75 | 76 | {clearance, changeNotification} = req.body 77 | body = {clearance, changeNotification} 78 | 79 | req.doc.updateAttributes body, (err) -> 80 | return next err if err 81 | res.send req.doc 82 | 83 | # expose clearanceCtl functions 84 | module.exports.sendAll = clearanceCtl.sendAll 85 | 86 | module.exports.contactList = clearanceCtl.contactList 87 | 88 | module.exports.contactPicture = clearanceCtl.contactPicture 89 | 90 | module.exports.contact = clearanceCtl.contact 91 | 92 | -------------------------------------------------------------------------------- /server/helpers/file.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | 3 | # Ensure that: 4 | # 5 | # * all path starts with / 6 | # * except empty path which is an empty string. 7 | normalizePath: (path) -> 8 | if path is "/" 9 | path = "" 10 | else if path.length > 0 and path[0] isnt '/' 11 | path = "/#{path}" 12 | path 13 | 14 | # Returns a file calss depending of the mime type. It's useful to render 15 | # icons properly. 16 | getFileClass: (file) -> 17 | type = file.headers['content-type'] 18 | switch type.split('/')[0] 19 | when 'image' then fileClass = "image" 20 | when 'application' then fileClass = "document" 21 | when 'text' then fileClass = "document" 22 | when 'audio' then fileClass = "music" 23 | when 'video' then fileClass = "video" 24 | else 25 | fileClass = "file" 26 | fileClass 27 | 28 | 29 | # Factory to create comparator to sort the content of a folder based on 30 | # criterion and order. 31 | folderContentComparatorFactory: (criterion, order) -> 32 | 33 | # Default values 34 | CRITERION = criterion or 'name' 35 | ORDER = order or 'asc' 36 | 37 | return (f1, f2) -> 38 | t1 = f1.docType.toLowerCase() 39 | t2 = f2.docType.toLowerCase() 40 | 41 | if t1 is t2 42 | 43 | if CRITERION is 'name' 44 | n1 = f1.name.toLocaleLowerCase() 45 | n2 = f2.name.toLocaleLowerCase() 46 | else if CRITERION is "lastModification" 47 | n1 = new Date(f1.lastModification).getTime() 48 | n2 = new Date(f2.lastModification).getTime() 49 | else 50 | n1 = f1[CRITERION] 51 | n2 = f2[CRITERION] 52 | 53 | sort = if ORDER is 'asc' then -1 else 1 54 | if CRITERION is 'class' and n1 is n2 55 | 56 | # Sort by name if the class is the same 57 | n1 = f1.name.toLocaleLowerCase() 58 | n2 = f2.name.toLocaleLowerCase() 59 | 60 | # Get both file extensions 61 | e1 = n1.split('.').pop() 62 | e2 = n2.split('.').pop() 63 | 64 | if e1 isnt e2 65 | # Sort by file extension if they are different 66 | if e1 > e2 then return -sort 67 | if e1 < e2 then return sort 68 | return 0 69 | 70 | # Sort normally 71 | if n1 > n2 then return -sort 72 | else if n1 < n2 then return sort 73 | else return 0 74 | 75 | else if t1 is 'file' and t2 is 'folder' 76 | return 1 77 | else # t1 is 'folder' and t2 is 'file' 78 | return -1 79 | -------------------------------------------------------------------------------- /server/helpers/get_template_ext.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | path = require 'path' 3 | 4 | # if the app is running from build/ it uses JS, otherwise it uses jade 5 | module.exports = -> 6 | filePath = path.resolve __dirname, "../views/index.js" 7 | if fs.existsSync(filePath) 8 | ext = 'js' 9 | else 10 | ext = 'jade' 11 | 12 | return ext 13 | -------------------------------------------------------------------------------- /server/helpers/init.coffee: -------------------------------------------------------------------------------- 1 | File = require '../models/file' 2 | Folder = require '../models/folder' 3 | async = require 'async' 4 | 5 | 6 | # Update index 7 | # Usefull for file/folder added by a device when app was stopped 8 | module.exports.updateIndex = (callback) -> 9 | File.all (err, files) -> 10 | if err 11 | console.log err 12 | else 13 | async.eachSeries files, (file) -> 14 | file.index ['name'], -> 15 | , -> 16 | Folder.all (err, folders) -> 17 | if err 18 | console.log err 19 | else 20 | async.eachSeries folders, (folder) -> 21 | folder.index ['name'], -> 22 | , -> 23 | console.log 'Re-indexation is done.' 24 | callback() if callback 25 | -------------------------------------------------------------------------------- /server/helpers/path.coffee: -------------------------------------------------------------------------------- 1 | # Compare file path to existing file paths. If it already exists, it return 2 | # false else it returns true. 3 | module.exports.checkIfPathAvailable = (fileInfo, files, exceptionId) -> 4 | fullPath = "#{fileInfo.path}/#{fileInfo.name}" 5 | for fileDoc in files 6 | fileFullPath = "#{fileDoc.path}/#{fileDoc.name}" 7 | if (fullPath is fileFullPath) and (fileDoc.id isnt exceptionId) 8 | return false 9 | return true 10 | -------------------------------------------------------------------------------- /server/helpers/update_parents.coffee: -------------------------------------------------------------------------------- 1 | async = require('async') 2 | log = require('printit') 3 | prefix: 'updateParents' 4 | 5 | # To avoid flooding CouchDB when doing a lot of updates in a folder, 6 | # we bulk the parent last modification update and do it asynchronously. 7 | parentFolders = {} 8 | timeout = null 9 | 10 | 11 | # Keep a reference to a parent folder 12 | module.exports.add = (folder, lastModification) -> 13 | folder.lastModification = lastModification 14 | fullpath = "#{folder.path}/#{folder.name}" 15 | parentFolders[fullpath] = 16 | folder: folder 17 | lastModification: lastModification 18 | module.exports.resetTimeout() 19 | 20 | 21 | # After 1 minute of inactivity, update parents 22 | module.exports.resetTimeout = -> 23 | clearTimeout(timeout) if timeout? 24 | timeout = setTimeout module.exports.flush, 60 * 1000 25 | 26 | 27 | # Save in RAM lastModification date for parents 28 | # Update folder parent once all files are uploaded 29 | module.exports.flush = (callback) -> 30 | folders = parentFolders 31 | parentFolders = {} 32 | async.forEachOfSeries folders, (entry, fullpath, done) -> 33 | data = lastModification: entry.lastModification 34 | entry.folder.updateAttributes data, (err) -> 35 | log.error err if err? 36 | done() 37 | , callback 38 | -------------------------------------------------------------------------------- /server/initialize.coffee: -------------------------------------------------------------------------------- 1 | localization = require './lib/localization_manager' 2 | RealtimeAdapter = require 'cozy-realtime-adapter' 3 | File = require './models/file' 4 | Folder = require './models/folder' 5 | feed = require './lib/feed' 6 | init = require './helpers/init' 7 | 8 | module.exports.beforeStart = (callback) -> 9 | localization.initialize callback 10 | 11 | module.exports.afterStart = (app, server, callback) -> 12 | 13 | feed.initialize server 14 | 15 | # retrieve locale and set polyglot object 16 | # notification events should be proxied to client 17 | realtime = RealtimeAdapter server, ['file.*', 'folder.*', 'contact.*'], \ 18 | path: '/public/socket.io' # expose socket.io on public side 19 | 20 | init.updateIndex() 21 | 22 | updateIndex = (type, id)-> 23 | type.find id, (err, file) -> 24 | if err 25 | return console.log "updateIndex err", err.stack if err 26 | unless file 27 | return console.log "updateIndex : no file", id 28 | file.index ['name'], (err) -> 29 | # ignore this error 30 | # most likely caused by 31 | # remove binary -> update doc -> re-index 32 | # \-> remove doc -> but not doc 33 | 34 | realtime.on 'file.create', (event, id) -> 35 | updateIndex(File,id) 36 | realtime.on 'folder.create', (event, id) -> 37 | updateIndex(Folder, id) 38 | realtime.on 'file.update', (event, id) -> 39 | updateIndex(File, id) 40 | realtime.on 'folder.update', (event, id) -> 41 | updateIndex(Folder, id) 42 | 43 | callback app, server if callback? 44 | -------------------------------------------------------------------------------- /server/lib/feed.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class Feed 2 | 3 | axonSock: undefined 4 | 5 | constructor: -> 6 | @logger = require('printit') 7 | date: true 8 | prefix: 'helper/db_feed' 9 | 10 | initialize: (server) -> 11 | @startPublishingToAxon() 12 | 13 | server.on 'close', => 14 | @axonSock.close() if @axonSock? 15 | 16 | startPublishingToAxon: -> 17 | axon = require 'axon' 18 | @axonSock = axon.socket 'pub' 19 | axonPort = parseInt process.env.AXON_PORT or 9105 20 | @axonSock.connect axonPort 21 | @logger.info 'Pub server started' 22 | 23 | publish: (event, id) => 24 | @logger.info "Publishing #{event} #{id}" 25 | @axonSock.send event, id if @axonSock? 26 | 27 | module.exports = new Feed() 28 | -------------------------------------------------------------------------------- /server/lib/localization_manager.coffee: -------------------------------------------------------------------------------- 1 | jade = require 'jade' 2 | fs = require 'fs' 3 | cozydb = require 'cozydb' 4 | Polyglot = require 'node-polyglot' 5 | 6 | getTemplateExt = require '../helpers/get_template_ext' 7 | ext = getTemplateExt() 8 | 9 | class LocalizationManager 10 | 11 | polyglot: null 12 | 13 | # should be run when app starts 14 | initialize: (callback = ->) -> 15 | @retrieveLocale (err, locale) => 16 | if err? 17 | @polyglot = @getPolyglotByLocale null 18 | else 19 | @polyglot = @getPolyglotByLocale locale 20 | callback null, @polyglot 21 | 22 | retrieveLocale: (callback) -> 23 | cozydb.api.getCozyLocale (err, locale) -> 24 | if err? or not locale then locale = 'en' # default value 25 | callback err, locale 26 | 27 | getPolyglotByLocale: (locale) -> 28 | if locale? 29 | try 30 | phrases = require "../locales/#{locale}" 31 | catch err 32 | phrases = require '../locales/en' 33 | else 34 | phrases = require '../locales/en' 35 | return new Polyglot locale: locale, phrases: phrases 36 | 37 | # execute polyglot.t, for server-side localization 38 | t: (key, params = {}) -> return @polyglot?.t key, params 39 | 40 | getEmailTemplate: (name) -> 41 | # get path of template in a language 42 | getPath = (lang) -> 43 | filePath = "../views/#{lang}/#{name}" 44 | templatefile = require('path').join __dirname, filePath 45 | if ext isnt 'jade' 46 | templatefile = templatefile.replace('jade', 'js') 47 | if fs.existsSync(templatefile) 48 | return templatefile 49 | else 50 | null 51 | 52 | # If template doesn't exists, use english as default 53 | templatePath = getPath @polyglot.currentLocale 54 | if not templatePath? 55 | templatePath = getPath 'en' 56 | 57 | if ext is 'jade' 58 | return jade.compile fs.readFileSync templatePath, 'utf8' 59 | else 60 | return require(templatePath) 61 | 62 | # for template localization 63 | getPolyglot: -> return @polyglot 64 | 65 | module.exports = new LocalizationManager() 66 | -------------------------------------------------------------------------------- /server/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "default user name": "Someone", 3 | "email sharing subject": "%{displayName} shared \"%{name}\" with you", 4 | "email change subject": "%{displayName}'s Cozy: folder \"%{itemName}\" changed", 5 | "notification new file": "%{who} uploaded file %{fileName} into %{folderName}", 6 | "file": "file", 7 | "folder": "folder", 8 | "view file": "View file", 9 | "view folder": "Browse folder", 10 | "link file content": "Click here to view this file", 11 | "link folder content": "Click here to browse this folder", 12 | "404 headline": "Oops, this file cannot be found.", 13 | "404 option a": "You have mistyped the URL address", 14 | "404 option separator": "or", 15 | "404 option b": "The Cozy's owner may have deleted the related file", 16 | "404 clippy sorry": "Sorry, I couldn't find the file you are looking for", 17 | "404 clippy contact": "You may want to contact the Cozy's owner!" 18 | } 19 | -------------------------------------------------------------------------------- /server/locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "default user name": "Quelqu'un", 3 | "email sharing subject": "%{displayName} a partagé \"%{name}\" avec vous", 4 | "email change subject": "Cozy de %{displayName}: le dossier \"%{itemName}\" a changé", 5 | "notification new file": "%{who} a ajouté le fichier %{fileName} dans le dossier %{folderName}", 6 | "file": "fichier", 7 | "folder": "dossier", 8 | "view file": "Afficher le fichier", 9 | "view folder": "Parcourir le dossier", 10 | "link file content": "Cliquez ici pour voir ce fichier", 11 | "link folder content": "Cliquez ici pour naviguer dans ce dossier", 12 | "404 headline": "Oups, ce fichier n'a pas pu être trouvé.", 13 | "404 option a": "Vous avez fait une erreur en tapant l'adresse URL", 14 | "404 option separator": "ou", 15 | "404 option b": "Le propriétaire de ce Cozy a supprimé le fichier correspondant", 16 | "404 clippy sorry": "Désolé, je n'ai pas pu trouver le fichier que vous cherchez", 17 | "404 clippy contact": "Vous pouvez toujours contacter le propriétaire de ce Cozy !" 18 | } 19 | -------------------------------------------------------------------------------- /server/middlewares/errors.coffee: -------------------------------------------------------------------------------- 1 | util = require 'util' 2 | logger = require('printit') 3 | date: true 4 | 5 | module.exports = (err, req, res, next) -> 6 | 7 | statusCode = err.status or 500 8 | message = if err instanceof Error then err.message else err.error 9 | message = message or 'Server error occurred' # default message 10 | 11 | if err.headers? and Object.keys(err.headers).length > 0 12 | res.set header, value for header, value of err.headers 13 | 14 | if err.template? and req?.accepts('html') is 'html' 15 | templateName = "#{err.template.name}" 16 | res.render templateName, err.template.params, (err, html) -> 17 | res.status(statusCode).send html 18 | else 19 | res.status(statusCode).send error: message 20 | 21 | if err instanceof Error 22 | logger.error err.message 23 | logger.error err.stack 24 | -------------------------------------------------------------------------------- /server/middlewares/public_auth.coffee: -------------------------------------------------------------------------------- 1 | Folder = require '../models/folder' 2 | File = require '../models/file' 3 | 4 | sharing = require '../helpers/sharing' 5 | 6 | module.exports.checkClearance = (permission, type) -> (req, res, next) -> 7 | if req.folder? 8 | element = new Folder req.folder 9 | else if req.file? 10 | element = new File req.file 11 | else if type is 'folder' 12 | element = new Folder req.body 13 | else 14 | element = new File req.body 15 | 16 | sharing.checkClearance element, req, permission, (authorized, rule) -> 17 | if authorized 18 | if rule? 19 | req.guestEmail = rule.email 20 | req.guestId = rule.contactid 21 | next() 22 | else 23 | err = new Error 'You cannot access this resource' 24 | err.status = 404 25 | err.template = 26 | name: '404' 27 | params: 28 | localization: require '../lib/localization_manager' 29 | isPublic: true 30 | next err 31 | -------------------------------------------------------------------------------- /server/models/binary.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | module.exports = Binary = cozydb.getModel 'Binary', cozydb.NoSchema 4 | -------------------------------------------------------------------------------- /server/models/contact.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | class DataPoint extends cozydb.Model 4 | @schema: 5 | name: String 6 | value: String 7 | type: String 8 | 9 | module.exports = Contact = cozydb.getModel 'Contact', 10 | fn : String 11 | n : String 12 | datapoints : [DataPoint] -------------------------------------------------------------------------------- /server/models/requests.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | module.exports = 4 | file: 5 | all: cozydb.defaultRequests.all 6 | byTag: cozydb.defaultRequests.tags 7 | byFolder: cozydb.defaultRequests.by 'path' 8 | byFullPath: (doc) -> emit (doc.path + '/' + doc.name), doc 9 | folder: 10 | all: cozydb.defaultRequests.all 11 | byTag: cozydb.defaultRequests.tags 12 | byFolder: cozydb.defaultRequests.by 'path' 13 | byFullPath: (doc) -> emit (doc.path + '/' + doc.name), doc 14 | 15 | contact: 16 | all: cozydb.defaultRequests.all 17 | -------------------------------------------------------------------------------- /server/models/user.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | 4 | hideEmail = (email) -> 5 | email.split('@')[0] 6 | .replace '.', ' ' 7 | .replace '-', ' ' 8 | 9 | 10 | # Get User Information 11 | # 12 | # This method get current cozy user from the CozyDB API and return a filtered 13 | # object containing the user name and email. 14 | # 15 | # @returns obj 16 | # name: string 17 | # email: string 18 | # 19 | getUserInfo = (callback) -> 20 | cozydb.api.getCozyUser (err, user) -> 21 | return callback err if err 22 | 23 | name = if user.public_name?.length 24 | user.public_name 25 | else 26 | words = hideEmail(user.email).split ' ' 27 | words.map((word) -> word[0].toUpperCase() + word[1...]).join ' ' 28 | 29 | callback null, 30 | name: name 31 | email: user.email 32 | 33 | module.exports.getUserInfo = getUserInfo 34 | 35 | 36 | # Get Display Name 37 | # 38 | # This method is just a convenient wrapper around getUserInfo method that 39 | # execute the given callback on the user.name only. 40 | module.exports.getDisplayName = (callback) -> 41 | getUserInfo (err, user) -> 42 | return callback err if err 43 | callback null, user.name 44 | -------------------------------------------------------------------------------- /server/views/404.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title 404 - File not found 5 | link(rel="stylesheet", type="text/css", href="stylesheets/app.css", media="all") 6 | link(rel="stylesheet", type="text/css", href="stylesheets/clippy.css", media="all") 7 | 8 | body.error-404 9 | .container 10 | .row 11 | .col-lg-12.error-frame 12 | p.headline= localization.t('404 headline') 13 | p= localization.t('404 option a') 14 | p= localization.t('404 option separator') 15 | p= localization.t('404 option b') 16 | 17 | script(src="javascripts/vendor.js") 18 | script(src="javascripts/clippy.min.js") 19 | script. 20 | clippy.load('Clippy', function(agent) { 21 | agent.show() 22 | agent.speak("#{localization.t('404 clippy sorry')}"); 23 | setTimeout(function() { 24 | agent.speak("#{localization.t('404 clippy contact')}"); 25 | setTimeout(function() { 26 | agent.play("SendMail"); 27 | setInterval(function() { 28 | agent.animate(); 29 | }, 15000); 30 | }, 5000); 31 | }, 5000); 32 | 33 | }); -------------------------------------------------------------------------------- /server/views/404_build.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title 404 - File not found 5 | link(rel="stylesheet", type="text/css", href="DIGEST(stylesheets/app.css)", media="all") 6 | link(rel="stylesheet", type="text/css", href="stylesheets/clippy.css", media="all") 7 | 8 | body.error-404 9 | .container 10 | .row 11 | .col-lg-12.error-frame 12 | p.headline= localization.t('404 headline') 13 | p= localization.t('404 option a') 14 | p= localization.t('404 option separator') 15 | p= localization.t('404 option b') 16 | 17 | script(src="DIGEST(javascripts/vendor.js)") 18 | script(src="javascripts/clippy.min.js") 19 | script. 20 | clippy.load('Clippy', function(agent) { 21 | agent.show() 22 | agent.speak("#{localization.t('404 clippy sorry')}"); 23 | setTimeout(function() { 24 | agent.speak("#{localization.t('404 clippy contact')}"); 25 | setTimeout(function() { 26 | agent.play("SendMail"); 27 | setInterval(function() { 28 | agent.animate(); 29 | }, 15000); 30 | }, 5000); 31 | }, 5000); 32 | 33 | }); -------------------------------------------------------------------------------- /server/views/en/notifmail.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | meta(name="viewport", content="width=device-width") 5 | meta(http-equiv="Content-Type", content="text/html; charset=UTF-8") 6 | 7 | body 8 | table(cellspacing="0", style="border-radius: 5px; border: 5px solid #34A6FF; margin: auto; width: 75%; padding: 20px 20px 10px 20px") 9 | tr 10 | th(style="color: #34A6FF; font-size: 20px") The content of "#{name}" has changed. 11 | tr 12 | td(style="padding: 45px 0; text-align: center; font-size: 18px") 13 | a(href=url, style="background: #34A6FF; color: #fff; padding: 8px; border-radius: 5px; text-decoration: none;") 14 | | #{localization.t('link folder content')} ! 15 | tr 16 | td(style="text-align: center;") 17 | | To unsubscribe from these notifications,  18 | a(href=url + '¬ifications=false', style="color: #34A6FF;") click here 19 | | . 20 | 21 | tr 22 | td(style="text-align: right; font-size: 12px; padding-top: 20px;") 23 | | Sent from #{displayName}'s  24 | a(href="http://cozy.io", style="color: #34A6FF;") Cozy 25 | | . 26 | -------------------------------------------------------------------------------- /server/views/en/sharemail.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html(style="height:100%;") 3 | head 4 | meta(name="viewport", content="width=device-width") 5 | meta(http-equiv="Content-Type", content="text/html; charset=UTF-8") 6 | 7 | body(style="height:100%; margin:0;") 8 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; height: 100%; padding:0; margin:0; background-color: #F4F4F4; font-family: Helvetica,Arial,Verdana,sans-serif; color: #333; font-size: 0.9em; line-height: 1.6;") 9 | tr 10 | td(align="center", valign="top") 11 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; padding: 2em 0.5em;") 12 | tr 13 | td 14 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; background-color: #FFF; border-radius: 8px; border: 1px solid #DDD;") 15 | tr 16 | th(style="border-radius: 8px 8px 0 0; border-bottom: 1px solid #DDD;") 17 | img(src="cid:cozy-logo" width="63" height="48" style="padding: 12px 0; margin: auto;") 18 | tr 19 | // TODO: add user's email inside the brackets #{displayEmail} 20 | td(style="padding: 1.5em;") 21 | | #{displayName} (#{displayEmail}) shared a #{localization.t(type)} called 22 | a(href=url, style="color: #34A6FF; text-decoration: none;") "#{doc.name}" 23 | | with you via Cozy Cloud. 24 | tr 25 | td(style="padding: 0.5em 1.5em 2em; width: 100%;") 26 | a(href=url, style="display: block; width: 60%; margin: 0 auto; padding: 1.2em 2em; background-color: #34A6FF; border-radius: 5px; color: #FFF; text-decoration: none; text-align: center; font-size: 1em; font-weight: bold; cursor:pointer;") 27 | | #{displayLabel} 28 | if type == 'folder' 29 | tr 30 | td(style="padding: 0em 1.5em 1.5em; color: #999;") 31 | | You can: 32 | ul(style="margin: 0;") 33 | li Download files from this folder 34 | if type == 'folder' && rule.perm == 'rw' 35 | li Add new files to this folder 36 | li Subscribe to change notifications of this folder 37 | tr(style="padding-top: 10px; text-align: center; font-size: 0.85em; font-style: italic; color: #999;") 38 | td 39 | p 40 | | Sent from  41 | a(href="http://cozy.io", style="color: #34A6FF; text-decoration: none;") #{displayName}'s Cozy 42 | | . 43 | -------------------------------------------------------------------------------- /server/views/fr/notifmail.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | meta(name="viewport", content="width=device-width") 5 | meta(http-equiv="Content-Type", content="text/html; charset=UTF-8") 6 | 7 | body 8 | table(cellspacing="0", style="border-radius: 5px; border: 5px solid #34A6FF; margin: auto; width: 75%; padding: 20px 20px 10px 20px") 9 | tr 10 | th(style="color: #34A6FF; font-size: 20px") Le contenu du dossier "#{name}" a changé. 11 | tr 12 | td(style="padding: 45px 0; text-align: center; font-size: 18px") 13 | a(href=url, style="background: #34A6FF; color: #fff; padding: 8px; border-radius: 5px; text-decoration: none;") 14 | | #{localization.t('link folder content')} ! 15 | tr 16 | td(style="text-align: center;") 17 | | Pour vous désabonner de ces notifications,  18 | a(href=url + '¬ifications=false', style="color: #34A6FF;") cliquez ici 19 | | . 20 | 21 | tr 22 | td(style="text-align: right; font-size: 12px; padding-top: 20px;") 23 | | Envoyé depuis le  24 | a(href="http://cozy.io", style="color: #34A6FF;") Cozy 25 | |  de #{displayName}. 26 | -------------------------------------------------------------------------------- /server/views/fr/sharemail.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html(style="height:100%;") 3 | head 4 | meta(name="viewport", content="width=device-width") 5 | meta(http-equiv="Content-Type", content="text/html; charset=UTF-8") 6 | 7 | body(style="height:100%; margin:0;") 8 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; height: 100%; padding:0; margin:0; background-color: #F4F4F4; font-family: Helvetica,Arial,Verdana,sans-serif; color: #333; font-size: 0.9em; line-height: 1.6;") 9 | tr 10 | td(align="center", valign="top") 11 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; padding: 2em 0.5em;") 12 | tr 13 | td 14 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; background-color: #FFF; border-radius: 8px; border: 1px solid #DDD;") 15 | tr 16 | th(style="border-radius: 8px 8px 0 0; border-bottom: 1px solid #DDD;") 17 | img(src="cid:cozy-logo" width="63" height="48" style="padding: 12px 0; margin: auto;") 18 | tr 19 | // TODO: add user's email inside the brackets #{displayEmail} 20 | td(style="padding: 1.5em;") 21 | | #{displayName} (#{displayEmail}) a partagé le #{localization.t(type)} appelé 22 | a(href=url, style="color: #34A6FF; text-decoration: none;") "#{doc.name}" 23 | | avec vous via Cozy Cloud. 24 | tr 25 | td(style="padding: 0.5em 1.5em 2em; width: 100%;") 26 | a(href=url, style="display: block; width: 60%; margin: 0 auto; padding: 1.2em 2em; background-color: #34A6FF; border-radius: 5px; color: #FFF; text-decoration: none; text-align: center; font-size: 1em; font-weight: bold; cursor:pointer;") 27 | | #{displayLabel} 28 | if type == 'folder' 29 | tr 30 | td(style="padding: 0em 1.5em 1.5em; color: #999;") 31 | | Vous pouvez: 32 | ul(style="margin: 0;") 33 | li Télécharger les fichiers de ce dossier 34 | if type == 'folder' && rule.perm == 'rw' 35 | li Ajouter des fichiers à ce dossier 36 | li Vous abonner aux notifications de changement de ce dossier 37 | tr(style="padding-top: 10px; text-align: center; font-size: 0.85em; font-style: italic; color: #999;") 38 | td 39 | p 40 | | Envoyé depuis le  41 | a(href="http://cozy.io", style="color: #34A6FF; text-decoration: none;") Cozy de #{displayName} 42 | | . 43 | -------------------------------------------------------------------------------- /server/views/index.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | meta(charset="utf-8") 5 | meta(http-equiv="X-UA-Compatible",content="IE=edge, chrome=1") 6 | meta(name="viewport", content="width=device-width, initial-scale=1") 7 | 8 | title Cozy - Files 9 | 10 | link(rel="apple-touch-icon", sizes="57x57", href="/apple-touch-icon-57x57.png") 11 | link(rel="apple-touch-icon", sizes="60x60", href="/apple-touch-icon-60x60.png") 12 | link(rel="apple-touch-icon", sizes="72x72", href="/apple-touch-icon-72x72.png") 13 | link(rel="apple-touch-icon", sizes="76x76", href="/apple-touch-icon-76x76.png") 14 | link(rel="apple-touch-icon", sizes="114x114", href="/apple-touch-icon-114x114.png") 15 | link(rel="apple-touch-icon", sizes="120x120", href="/apple-touch-icon-120x120.png") 16 | link(rel="apple-touch-icon", sizes="144x144", href="/apple-touch-icon-144x144.png") 17 | link(rel="apple-touch-icon", sizes="152x152", href="/apple-touch-icon-152x152.png") 18 | link(rel="apple-touch-icon", sizes="180x180", href="/apple-touch-icon-180x180.png") 19 | link(rel="icon", type="image/png", href="/favicon-32x32.png", sizes="32x32") 20 | link(rel="icon", type="image/png", href="/favicon-194x194.png", sizes="194x194") 21 | link(rel="icon", type="image/png", href="/favicon-96x96.png", sizes="96x96") 22 | link(rel="icon", type="image/png", href="/android-chrome-192x192.png", sizes="192x192") 23 | link(rel="icon", type="image/png", href="/favicon-16x16.png", sizes="16x16") 24 | link(rel="manifest", href="/manifest.json") 25 | link(rel="shortcut icon", href="/favicon.ico") 26 | meta(name="msapplication-TileColor", content="#8cb1ff") 27 | meta(name="msapplication-TileImage", content="/mstile-144x144.png") 28 | meta(name="msapplication-config", content="/browserconfig.xml") 29 | meta(name="theme-color", content="#8cb1ff") 30 | 31 | script(src="javascripts/modernizr-2.6.1.js") 32 | link(rel="stylesheet", href="/fonts/fonts.css") 33 | link(rel="stylesheet", href="stylesheets/app.css") 34 | 35 | body.application 36 | 37 | script!= imports 38 | script(src="javascripts/vendor.js") 39 | script(src="javascripts/app.js", onload="require('initialize');") 40 | -------------------------------------------------------------------------------- /server/views/index_build.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | meta(charset="utf-8") 5 | meta(http-equiv="X-UA-Compatible",content="IE=edge, chrome=1") 6 | meta(name="viewport", content="width=device-width, initial-scale=1") 7 | 8 | title Cozy - Files 9 | 10 | link(rel="apple-touch-icon", sizes="57x57", href="/apps/files/apple-touch-icon-57x57.png") 11 | link(rel="apple-touch-icon", sizes="60x60", href="/apps/files/apple-touch-icon-60x60.png") 12 | link(rel="apple-touch-icon", sizes="72x72", href="/apps/files/apple-touch-icon-72x72.png") 13 | link(rel="apple-touch-icon", sizes="76x76", href="/apps/files/apple-touch-icon-76x76.png") 14 | link(rel="apple-touch-icon", sizes="114x114", href="/apps/files/apple-touch-icon-114x114.png") 15 | link(rel="apple-touch-icon", sizes="120x120", href="/apps/files/apple-touch-icon-120x120.png") 16 | link(rel="apple-touch-icon", sizes="144x144", href="/apps/files/apple-touch-icon-144x144.png") 17 | link(rel="apple-touch-icon", sizes="152x152", href="/apps/files/apple-touch-icon-152x152.png") 18 | link(rel="apple-touch-icon", sizes="180x180", href="/apps/files/apple-touch-icon-180x180.png") 19 | link(rel="icon", type="image/png", href="/apps/files/favicon-32x32.png", sizes="32x32") 20 | link(rel="icon", type="image/png", href="/apps/files/favicon-194x194.png", sizes="194x194") 21 | link(rel="icon", type="image/png", href="/apps/files/favicon-96x96.png", sizes="96x96") 22 | link(rel="icon", type="image/png", href="/apps/files/android-chrome-192x192.png", sizes="192x192") 23 | link(rel="icon", type="image/png", href="/apps/files/favicon-16x16.png", sizes="16x16") 24 | link(rel="manifest", href="/apps/files/manifest.json") 25 | link(rel="shortcut icon", href="/apps/files/favicon.ico") 26 | meta(name="msapplication-TileColor", content="#8cb1ff") 27 | meta(name="msapplication-TileImage", content="/apps/files/mstile-144x144.png") 28 | meta(name="msapplication-config", content="/apps/files/browserconfig.xml") 29 | meta(name="theme-color", content="#8cb1ff") 30 | 31 | script(src="javascripts/modernizr-2.6.1.js") 32 | link(rel="stylesheet", href="/fonts/fonts.css") 33 | link(rel="stylesheet", href="DIGEST(stylesheets/app.css)") 34 | 35 | body.application 36 | 37 | script!= imports 38 | script(src="DIGEST(javascripts/vendor.js)") 39 | script(src="DIGEST(javascripts/app.js)", onload="require('initialize');") 40 | -------------------------------------------------------------------------------- /test/create_batch_of_directories.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | path = require "path" 3 | 4 | 5 | ###* 6 | * PARAMETERS 7 | ### 8 | numberOfDirToCreate = process.argv[2] or 5 9 | folderTargetPath = './fixtures/big-folder-of-folders' 10 | content = fs.readFileSync './fixtures/files/test.txt' 11 | 12 | 13 | ###* 14 | * HELPERS 15 | ### 16 | rmdirSync = (dir) -> 17 | list = fs.readdirSync dir 18 | for item in list 19 | filename = path.join dir, item 20 | stat = fs.statSync filename 21 | if filename in [".", ".."] 22 | # Skip 23 | else if stat.isDirectory() 24 | # Remove directory recursively 25 | rmdirSync filename 26 | else 27 | # Remove file 28 | fs.unlinkSync filename 29 | fs.rmdirSync dir 30 | 31 | 32 | ###* 33 | * PREPARE THE FOLDER 34 | ### 35 | try 36 | stats = fs.lstatSync(folderTargetPath) 37 | console.log "dir exists, delete it" 38 | rmdirSync(folderTargetPath) 39 | catch e 40 | 41 | finally 42 | console.log "create dir" 43 | fs.mkdirSync(folderTargetPath) 44 | 45 | 46 | # fs.readdirSync('./fixtures/big-folder-of-folder') 47 | 48 | ###* 49 | * CREATE DIRECTORIES 50 | ### 51 | for i in [0..numberOfDirToCreate-1] by 1 52 | # rank = if i < 10 then "0#{i}" else i 53 | rkTXT = Array(7 - "#{i}".length).join('0') + i 54 | dirname = "test-#{rkTXT}" 55 | fs.mkdirSync folderTargetPath + '/' + dirname 56 | fs.writeFileSync folderTargetPath + '/' + dirname + '/afile.txt', content 57 | 58 | console.log numberOfDirToCreate, 'directories created in', folderTargetPath + '/' 59 | -------------------------------------------------------------------------------- /test/create_batch_of_files.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | path = require "path" 3 | 4 | 5 | ###* 6 | * PARAMETERS 7 | ### 8 | numberOfFilesToCreate = process.argv[2] or 5 9 | folderTargetPath = './fixtures/big-folder' 10 | content = fs.readFileSync './fixtures/files/test.txt' 11 | 12 | 13 | ###* 14 | * HELPERS 15 | ### 16 | rmdirSync = (dir) -> 17 | list = fs.readdirSync dir 18 | for item in list 19 | filename = path.join dir, item 20 | stat = fs.statSync filename 21 | if filename in [".", ".."] 22 | # Skip 23 | else if stat.isDirectory() 24 | # Remove directory recursively 25 | rmdirSync filename 26 | else 27 | # Remove file 28 | fs.unlinkSync filename 29 | fs.rmdirSync dir 30 | 31 | 32 | ###* 33 | * PREPARE THE FOLDER 34 | ### 35 | try 36 | stats = fs.lstatSync(folderTargetPath) 37 | console.log "dir exists, delete it" 38 | rmdirSync(folderTargetPath) 39 | catch e 40 | 41 | finally 42 | console.log "create dir" 43 | fs.mkdirSync(folderTargetPath) 44 | 45 | 46 | ###* 47 | * CREATE FILES 48 | ### 49 | for i in [0..numberOfFilesToCreate-1] by 1 50 | # rank = if i < 10 then "0#{i}" else i 51 | rkTXT = Array(7 - "#{i}".length).join('0') + i 52 | filename = "test-#{rkTXT}" 53 | fs.writeFileSync folderTargetPath + '/' + filename, content 54 | 55 | console.log numberOfFilesToCreate, 'files created in', folderTargetPath+'/' 56 | -------------------------------------------------------------------------------- /test/create_problematic_directory.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generate a random directory name 4 | dir_name=$(LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom | head -c 24 | xargs) 5 | mkdir $dir_name 6 | cd $dir_name 7 | 8 | # Generate 100 files with dd (of 200 bytes) with a random name 9 | for ((i = 1; i <= 100; i++)); do 10 | file_name=$(LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom | head -c 12 | xargs) 11 | dd if=/dev/urandom bs=1 count=200 of=./$file_name > /dev/null 2>&1 12 | done 13 | 14 | echo "Directory $dir_name created" 15 | -------------------------------------------------------------------------------- /test/create_problematic_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generate a random filename 4 | file_name=$(LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom | head -c 12 | xargs) 5 | 6 | # Populate file with dd 7 | dd if=/dev/zero bs=1 count=0 seek=2000000000 of=./$file_name > /dev/null 2>&1 8 | 9 | echo "File $file_name created" 10 | -------------------------------------------------------------------------------- /test/fixtures/files/chat-mignon1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/test/fixtures/files/chat-mignon1.jpg -------------------------------------------------------------------------------- /test/fixtures/files/chat-mignon2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/test/fixtures/files/chat-mignon2.jpg -------------------------------------------------------------------------------- /test/fixtures/files/chaton-innocent.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-files/873f7d8071a9fcc31ff06260b1a129c436f28b65/test/fixtures/files/chaton-innocent.jpg -------------------------------------------------------------------------------- /test/fixtures/files/test.txt: -------------------------------------------------------------------------------- 1 | { 2 | "path": "", 3 | "name": "test3", 4 | "id": "5f912a649bc1f76e757b078c9a0e4261" 5 | } -------------------------------------------------------------------------------- /test/fixtures/files/test2.txt: -------------------------------------------------------------------------------- 1 | Does overwrite work? 2 | -------------------------------------------------------------------------------- /test/fixtures/folders.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "docType": "Folder", 4 | "name": "Mes images", 5 | "path": "" 6 | }, 7 | { 8 | "docType": "Folder", 9 | "name": "Mes photos", 10 | "path": "" 11 | }, 12 | { 13 | "docType": "Folder", 14 | "name": "Mes vidéos", 15 | "path": "" 16 | }, 17 | { 18 | "docType": "Folder", 19 | "name": "Mes musiques", 20 | "path": "" 21 | }, 22 | { 23 | "docType": "Folder", 24 | "name": "Many files", 25 | "path": "" 26 | }, 27 | { 28 | "docType": "Folder", 29 | "name": "Files to remove", 30 | "path": "" 31 | }, 32 | { 33 | "docType": "Folder", 34 | "name": "Chats mignons", 35 | "path": "/Mes images" 36 | } 37 | ] -------------------------------------------------------------------------------- /test/helpers.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | Client = require('request-json').JsonClient 3 | 4 | helpers = {} 5 | if process.env.USE_JS 6 | helpers.prefix = path.join __dirname, '../build/' 7 | else 8 | helpers.prefix = path.join __dirname, '../' 9 | 10 | # server management 11 | helpers.options = 12 | serverHost: process.env.HOST or 'localhost' 13 | serverPort: process.env.PORT or 9121 14 | 15 | # default client 16 | client = new Client "http://#{helpers.options.serverHost}:#{helpers.options.serverPort}/", jar: true 17 | ds = new Client "http://localhost:9101/" 18 | ds.setBasicAuth process.env.NAME, process.env.TOKEN 19 | 20 | # set the configuration for the server 21 | process.env.HOST = helpers.options.serverHost 22 | process.env.PORT = helpers.options.serverPort 23 | 24 | # Returns a client if url is given, default app client otherwise 25 | helpers.getClient = (url = null) -> 26 | if url? 27 | return new Client url, jar: true 28 | else 29 | return client 30 | 31 | initializeApplication = require "#{helpers.prefix}server" 32 | CozyInstance = require("cozydb").api.CozyInstance 33 | 34 | helpers.ensureCozyInstance = (done) -> 35 | all = (doc) -> emit doc._id, doc; return 36 | CozyInstance.defineRequest 'all', all, (err) -> 37 | return done err if err 38 | CozyInstance.first (err, instance) -> 39 | return done null if instance 40 | CozyInstance.create 41 | domain: 'domain.not.set', 42 | locale: 'en' 43 | , done 44 | 45 | helpers.startApp = (done) -> 46 | @timeout 15000 47 | initializeApplication (app, server) => 48 | @app = app 49 | @app.server = server 50 | done() 51 | 52 | helpers.stopApp = (done) -> 53 | @timeout 10000 54 | setTimeout => 55 | @app.server.close done 56 | , 1000 57 | 58 | # Bring models in context 59 | File = require "#{helpers.prefix}server/models/file" 60 | Folder = require "#{helpers.prefix}server/models/folder" 61 | 62 | # This function remove everythin from the db 63 | helpers.cleanDB = (callback) -> 64 | @timeout 15000 65 | Folder.destroyAll (err) -> 66 | if err then callback err 67 | else File.destroyAll callback 68 | 69 | module.exports = helpers 70 | --------------------------------------------------------------------------------