├── .gitignore ├── .sandstorm ├── CHANGELOG.md ├── Vagrantfile ├── app-graphics │ ├── paperwork-128.svg │ ├── paperwork-150.svg │ ├── paperwork-24.svg │ ├── paperwork_example_01.png │ ├── paperwork_example_02.png │ └── paperwork_example_03.png ├── build.sh ├── description.md ├── global-setup.sh ├── launcher.sh ├── pgp-keyring ├── pgp-signature ├── sandstorm-files.list ├── sandstorm-pkgdef.capnp ├── sandstorm-screenshot.png ├── service-config │ ├── mime.types │ └── nginx.conf ├── setup.sh └── stack ├── LICENSE ├── README.md └── changedfiles ├── frontend └── app │ ├── config │ └── paperwork.php │ ├── controllers │ └── UserController.php │ ├── lang │ ├── de │ │ └── messages.php │ └── en │ │ └── messages.php │ ├── lib │ └── Paperwork │ │ └── Import │ │ └── EvernoteImport.php │ ├── routes.php │ └── views │ ├── main.blade.php │ ├── partials │ ├── debug-main.blade.php │ ├── menu-main.blade.php │ └── navigation-main.blade.php │ ├── templates │ └── paperworkNoteShow.blade.php │ └── user │ ├── debug.blade.php │ └── settings.blade.php └── storage ├── .gitignore ├── attachments └── .empty ├── cache └── .gitignore ├── config ├── .gitignore ├── database.json ├── paperwork.json └── setup ├── logs └── .gitignore ├── meta └── .gitignore ├── sessions └── .gitignore └── views └── .gitignore /.gitignore: -------------------------------------------------------------------------------- 1 | /bootstrap/compiled.php 2 | /vendor 3 | composer.phar 4 | composer.lock 5 | .env.*.php 6 | .env.php 7 | .DS_Store 8 | Thumbs.db 9 | .codekit-cache 10 | frontend/app/storage/attachments/* 11 | frontend/node_modules 12 | frontend/npm-debug.log 13 | frontend/public/css 14 | !frontend/public/css/bootstrap-editable.css 15 | frontend/public/js 16 | !frontend/public/js/bootstrap-editable.min.js 17 | .sandstorm/.vagrant 18 | frontend/app/storage 19 | frontend/install.sh 20 | frontend/bower_components 21 | *.spk 22 | *.zip 23 | /paperwork 24 | -------------------------------------------------------------------------------- /.sandstorm/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Version 8 2 | 3 | Bugfixes: 4 | - Some performance improvements due to update to latest Paperwork version 5 | - Fixed a blank screen issue due to update to latest Paperwork version https://github.com/JamborJan/paperwork/issues/45 6 | - Import issues fixed, see https://github.com/twostairs/paperwork/issues/667 (the import is still kind of weak) 7 | 8 | House keeping: 9 | - Improved build process to allow easier updates of the Sandstorm port when Paperwork is updated 10 | - Updated to latest Paperwork version 11 | - Updated the vagrant lemp-stack settings 12 | - Updated from nodejs version 5 to version 6 LTS 13 | 14 | ## Version 7 15 | This version has a new app ID. It requires Sandstorm v0.146 (2016-02-21) to allow seamless update to this version. 16 | 17 | Bugfixes: 18 | - You can use the app with retina displays now. Thanks @blutux ! (https://github.com/JamborJan/paperwork/commit/6e4e952cdcf097e32d011e834ef9c4b9dcc51263) 19 | - Note editor empty when opening note from filtered list https://github.com/JamborJan/paperwork/issues/31 20 | - Misspelling of encoding in exports -- seems fine but could it cause trouble? Thanks @erikmaarten ! https://github.com/JamborJan/paperwork/issues/29 21 | - Filtered note list gets lost after note is closed https://github.com/JamborJan/paperwork/issues/34 22 | - Make import robust against special characters in title https://github.com/JamborJan/paperwork/issues/27 23 | - Added missing fonts and language files https://github.com/JamborJan/paperwork/commit/d819714b3e6130b86f95438f80a24b65dba0162e 24 | 25 | House keeping: 26 | - Updated Vagrantfile with new base box (https://github.com/JamborJan/paperwork/commit/13262a822d98baf68b0f9b5ff54883f4fc0ff9ae) 27 | - Updated depreciated composer components (https://github.com/JamborJan/paperwork/commit/92ca5601daacf7cc978b99be72e75bb7dc2b0eb5) 28 | - Updated node version (https://github.com/JamborJan/paperwork/commit/0ff09728f4a4da0d4a260311841ab10141e252fc) 29 | - Updated to latest Sandstorm-pkgdef 30 | 31 | ## Version 6 32 | Skipped. 33 | 34 | ## Version 5 35 | This version has a new app ID. 36 | 37 | Changes: 38 | - latest source code from Paperwork used (2015.08.18) 39 | - new sharing Sandstorm sharing / permission features implemeted 40 | - several export / import fixes (still experimental) 41 | - new calendar view 42 | - way back machine is now working 43 | 44 | ## Version 4 45 | This version is deprecated but can be installed as update to version 3 and will show you an export screen when it detects an old grain. Afterwards you should install version 5 of the app and import the data again. 46 | 47 | ## Version 3 48 | This version is deprecated, exports are not working. To retrieve your data you will have to update to Version 4 and then export your data. 49 | -------------------------------------------------------------------------------- /.sandstorm/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | # We base ourselves off Debian Jessie 9 | config.vm.box = "sandstorm/debian-jessie64" 10 | 11 | # We forward port 6080, the Sandstorm web port, so that developers can 12 | # visit their sandstorm app from their browser as local.sandstorm.io:6080 13 | # (aka 127.0.0.1:6080). 14 | config.vm.network :forwarded_port, guest: 6080, host: 6080 15 | 16 | # Use a shell script to "provision" the box. This installs Sandstorm using 17 | # the bundled installer. 18 | config.vm.provision "shell", inline: "sudo bash /opt/app/.sandstorm/global-setup.sh" 19 | # Then, do stack-specific and app-specific setup. 20 | config.vm.provision "shell", inline: "sudo bash /opt/app/.sandstorm/setup.sh" 21 | 22 | # Shared folders are configured per-provider since vboxsf can't handle >4096 open files, 23 | # NFS requires privilege escalation every time you bring a VM up, 24 | # and 9p is only available on libvirt. 25 | 26 | # Calculate the number of CPUs and the amount of RAM the system has, 27 | # in a platform-dependent way; further logic below. 28 | cpus = nil 29 | total_kB_ram = nil 30 | 31 | host = RbConfig::CONFIG['host_os'] 32 | if host =~ /darwin/ 33 | cpus = `sysctl -n hw.ncpu`.to_i 34 | total_kB_ram = `sysctl -n hw.memsize`.to_i / 1024 35 | elsif host =~ /linux/ 36 | cpus = `nproc`.to_i 37 | total_kB_ram = `grep MemTotal /proc/meminfo | awk '{print $2}'`.to_i 38 | end 39 | # Use the same number of CPUs within Vagrant as the system, with 1 40 | # as a default. 41 | # 42 | # Use at least 512MB of RAM, and if the system has more than 2GB of 43 | # RAM, use 1/4 of the system RAM. This seems a reasonable compromise 44 | # between having the Vagrant guest operating system not run out of 45 | # RAM entirely (which it basically would if we went much lower than 46 | # 512MB) and also allowing it to use up a healthily large amount of 47 | # RAM so it can run faster on systems that can afford it. 48 | if cpus.nil? 49 | cpus = 1 50 | end 51 | if total_kB_ram.nil? or total_kB_ram < 2048000 52 | assign_ram_mb = 512 53 | else 54 | assign_ram_mb = (total_kB_ram / 1024 / 4) 55 | end 56 | # Actually apply these CPU/memory values to the providers. 57 | config.vm.provider :virtualbox do |vb, override| 58 | vb.cpus = cpus 59 | vb.memory = assign_ram_mb 60 | 61 | override.vm.synced_folder "..", "/opt/app" 62 | override.vm.synced_folder ENV["HOME"] + "/.sandstorm", "/host-dot-sandstorm" 63 | override.vm.synced_folder "..", "/vagrant" 64 | end 65 | config.vm.provider :libvirt do |libvirt, override| 66 | libvirt.cpus = cpus 67 | libvirt.memory = assign_ram_mb 68 | libvirt.random_hostname = true 69 | 70 | override.vm.synced_folder "..", "/opt/app", type: "9p", accessmode: "passthrough" 71 | override.vm.synced_folder ENV["HOME"] + "/.sandstorm", "/host-dot-sandstorm", type: "9p", accessmode: "passthrough" 72 | override.vm.synced_folder "..", "/vagrant", type: "9p", accessmode: "passthrough" 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /.sandstorm/app-graphics/paperwork-128.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 15 | 16 | 17 | 22 | 27 | 32 | 37 | 43 | 45 | 47 | 50 | 52 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /.sandstorm/app-graphics/paperwork-150.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 14 | 15 | 20 | 25 | 30 | 35 | 41 | 44 | 46 | 49 | 51 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /.sandstorm/app-graphics/paperwork-24.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 14 | 15 | 16 | 17 | 18 | 23 | 27 | 31 | 36 | 42 | 44 | 46 | 48 | 50 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /.sandstorm/app-graphics/paperwork_example_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamborJan/paperwork/14d35b48aad2171e54fdb95c9bddcc841d1eae82/.sandstorm/app-graphics/paperwork_example_01.png -------------------------------------------------------------------------------- /.sandstorm/app-graphics/paperwork_example_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamborJan/paperwork/14d35b48aad2171e54fdb95c9bddcc841d1eae82/.sandstorm/app-graphics/paperwork_example_02.png -------------------------------------------------------------------------------- /.sandstorm/app-graphics/paperwork_example_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamborJan/paperwork/14d35b48aad2171e54fdb95c9bddcc841d1eae82/.sandstorm/app-graphics/paperwork_example_03.png -------------------------------------------------------------------------------- /.sandstorm/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Checks if there's a composer.json, and if so, installs/runs composer. 3 | 4 | set -euo pipefail 5 | 6 | # Setup composer 7 | cd /opt/app/paperwork/frontend 8 | 9 | if [ -f /opt/app/paperwork/frontend/composer.json ] ; then 10 | if [ ! -f composer.phar ] ; then 11 | curl -sS https://getcomposer.org/installer | php 12 | fi 13 | php composer.phar install 14 | fi 15 | php composer.phar self-update 16 | 17 | # Install paperwork's npm dependencies 18 | npm install 19 | npm update 20 | 21 | # Install bower dependencies 22 | bower install 23 | 24 | # Run gulp to build static assets 25 | gulp 26 | 27 | # link storage folder 28 | rm -rf /opt/app/paperwork/frontend/app/storage 29 | rm -rf /var/storage 30 | ln -s /var/storage /opt/app/paperwork/frontend/app 31 | 32 | # Some files needed to be changed for ruinning Paperwork on Sandstorm 33 | cp -rf /opt/app/changedfiles/frontend/* /opt/app/paperwork/frontend 34 | -------------------------------------------------------------------------------- /.sandstorm/description.md: -------------------------------------------------------------------------------- 1 | **Paperwork is an open source self hosted note-taking & archiving app -- an alternative to Evernote, Microsoft OneNote & Google Keep.** 2 | 3 | Paperwork allows you to create and manage notes to boost your productivity. Use it as a daily companion to keep track of your tasks or as a knowledge management system and archive. Share notebooks with others to collaborate with read & write access or create a knowledge base where others have read-only access. 4 | 5 | This is a Sandstorm port of Paperwork based on [Twostairs Paperwork](http://paperwork.rocks/). 6 | -------------------------------------------------------------------------------- /.sandstorm/global-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu 3 | echo localhost > /etc/hostname 4 | hostname localhost 5 | curl https://install.sandstorm.io/ > /host-dot-sandstorm/caches/install.sh 6 | SANDSTORM_CURRENT_VERSION=$(curl -fs "https://install.sandstorm.io/dev?from=0&type=install") 7 | SANDSTORM_PACKAGE="sandstorm-$SANDSTORM_CURRENT_VERSION.tar.xz" 8 | if [[ ! -f /host-dot-sandstorm/caches/$SANDSTORM_PACKAGE ]] ; then 9 | curl --output "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE" "https://dl.sandstorm.io/$SANDSTORM_PACKAGE" 10 | fi 11 | bash /host-dot-sandstorm/caches/install.sh -d -e "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE" 12 | modprobe ip_tables 13 | # Make the vagrant user part of the sandstorm group so that commands like 14 | # `spk dev` work. 15 | usermod -a -G 'sandstorm' 'vagrant' 16 | # Bind to all addresses, so the vagrant port-forward works. 17 | sudo sed --in-place='' \ 18 | --expression='s/^BIND_IP=.*/BIND_IP=0.0.0.0/' \ 19 | /opt/sandstorm/sandstorm.conf 20 | # TODO: update sandstorm installer script to ask about dev accounts, and 21 | # specify a value for this option in the default config? 22 | if ! grep --quiet --no-messages ALLOW_DEV_ACCOUNTS=true /opt/sandstorm/sandstorm.conf ; then 23 | echo "ALLOW_DEV_ACCOUNTS=true" | sudo tee -a /opt/sandstorm/sandstorm.conf 24 | sudo service sandstorm restart 25 | fi 26 | # Enable apt-cacher-ng proxy to make things faster if one appears to be running on the gateway IP 27 | GATEWAY_IP=$(ip route | grep ^default | cut -d ' ' -f 3) 28 | if nc -z "$GATEWAY_IP" 3142 ; then 29 | echo "Acquire::http::Proxy \"http://$GATEWAY_IP:3142\";" > /etc/apt/apt.conf.d/80httpproxy 30 | fi 31 | -------------------------------------------------------------------------------- /.sandstorm/launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create a bunch of folders under the clean /var that php, nginx, and mysql expect to exist 4 | mkdir -p /var/lib/mysql 5 | mkdir -p /var/lib/nginx 6 | mkdir -p /var/lib/php5/sessions 7 | mkdir -p /var/log 8 | mkdir -p /var/log/mysql 9 | mkdir -p /var/log/nginx 10 | # Wipe /var/run, since pidfiles and socket files from previous launches should go away 11 | # TODO someday: I'd prefer a tmpfs for these. 12 | rm -rf /var/run 13 | mkdir -p /var/run 14 | rm -rf /var/tmp 15 | mkdir -p /var/tmp 16 | mkdir -p /var/run/mysqld 17 | 18 | # cleanup for update of older grains 19 | rm -rf /var/storage/config/setup 20 | rm /var/storage/paperwork_settings 21 | rm /var/storage/db_settings 22 | 23 | # copy storage folders which must be writable to /var 24 | cp -r /opt/app/changedfiles/storage /var 25 | chmod 777 -R /var/storage 26 | 27 | # Cleanup log files 28 | FILES="$(find /var/log -name '*.log')" 29 | for f in $FILES 30 | do 31 | tail $f | tee $f 32 | done 33 | 34 | # Ensure mysql tables created 35 | HOME=/etc/mysql /usr/bin/mysql_install_db --force 36 | 37 | # Spawn mysqld, php 38 | HOME=/etc/mysql /usr/sbin/mysqld & 39 | /usr/sbin/php5-fpm --nodaemonize --fpm-config /etc/php5/fpm/php-fpm.conf & 40 | # Wait until mysql and php have bound their sockets, indicating readiness 41 | while [ ! -e /var/run/mysqld/mysqld.sock ] ; do 42 | echo "waiting for mysql to be available at /var/run/mysqld/mysqld.sock" 43 | sleep .2 44 | done 45 | while [ ! -e /var/run/php5-fpm.sock ] ; do 46 | echo "waiting for php5-fpm to be available at /var/run/php5-fpm.sock" 47 | sleep .2 48 | done 49 | 50 | # Ensure the paperwork database exists. 51 | echo "CREATE DATABASE IF NOT EXISTS paperwork DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; GRANT ALL PRIVILEGES ON paperwork.* TO 'paperwork'@'localhost' IDENTIFIED BY 'paperwork' WITH GRANT OPTION; FLUSH PRIVILEGES;" | mysql --user root --socket /var/run/mysqld/mysqld.sock 52 | # Run database migrations. 53 | time php /opt/app/paperwork/frontend/artisan migrate --force 54 | 55 | # Start nginx. 56 | /usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;" 57 | -------------------------------------------------------------------------------- /.sandstorm/pgp-keyring: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamborJan/paperwork/14d35b48aad2171e54fdb95c9bddcc841d1eae82/.sandstorm/pgp-keyring -------------------------------------------------------------------------------- /.sandstorm/pgp-signature: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamborJan/paperwork/14d35b48aad2171e54fdb95c9bddcc841d1eae82/.sandstorm/pgp-signature -------------------------------------------------------------------------------- /.sandstorm/sandstorm-pkgdef.capnp: -------------------------------------------------------------------------------- 1 | @0xaedf071934889091; 2 | 3 | using Spk = import "/sandstorm/package.capnp"; 4 | # This imports: 5 | # $SANDSTORM_HOME/latest/usr/include/sandstorm/package.capnp 6 | # Check out that file to see the full, documented package definition format. 7 | 8 | const pkgdef :Spk.PackageDefinition = ( 9 | # The package definition. Note that the spk tool looks specifically for the 10 | # "pkgdef" constant. 11 | 12 | id = "vxe8awcxvtj6yu0vgjpm1tsaeu7x8v8tfp71tyvnm6ykkephu9q0", 13 | # Your app ID is actually its public key. The private key was placed in 14 | # your keyring. All updates must be signed with the same key. 15 | 16 | manifest = ( 17 | # This manifest is included in your app package to tell Sandstorm 18 | # about your app. 19 | 20 | appTitle = (defaultText = "Paperwork"), 21 | 22 | appVersion = 9, # Increment this for every release. 23 | 24 | appMarketingVersion = (defaultText = "2017.09.01"), 25 | # Human-readable representation of appVersion. Should match the way you 26 | # identify versions of your app in documentation and marketing. 27 | 28 | actions = [ 29 | # Define your "new document" handlers here. 30 | ( title = (defaultText = "New Paperwork notebook"), 31 | command = .myCommand 32 | # The command to run when starting for the first time. (".myCommand" 33 | # is just a constant defined at the bottom of the file.) 34 | ) 35 | ], 36 | 37 | continueCommand = .myCommand, 38 | # This is the command called to start your app back up after it has been 39 | # shut down for inactivity. Here we're using the same command as for 40 | # starting a new instance, but you could use different commands for each 41 | # case. 42 | 43 | metadata = ( 44 | # Data which is not needed specifically to execute the app, but is useful 45 | # for purposes like marketing and display. These fields are documented at 46 | # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#add-required-metadata 47 | # and (in deeper detail) in the sandstorm source code, in the Metadata section of 48 | # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/package.capnp 49 | icons = ( 50 | # Various icons to represent the app in various contexts. 51 | appGrid = (svg = embed "app-graphics/paperwork-128.svg"), 52 | grain = (svg = embed "app-graphics/paperwork-24.svg"), 53 | market = (svg = embed "app-graphics/paperwork-150.svg"), 54 | marketBig = (svg = embed "app-graphics/paperwork-150.svg"), 55 | ), 56 | 57 | website = "http://paperwork.rocks/", 58 | # This should be the app's main website url. 59 | 60 | codeUrl = "https://github.com/JamborJan/paperwork", 61 | # URL of the app's source code repository, e.g. a GitHub URL. 62 | # Required if you specify a license requiring redistributing code, but optional otherwise. 63 | 64 | license = (openSource = mit), 65 | # The license this package is distributed under. See 66 | # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#license 67 | 68 | categories = [productivity], 69 | # A list of categories/genres to which this app belongs, sorted with best fit first. 70 | # See the list of categories at 71 | # https://docs.sandstorm.io/en/latest/developing/publishing-apps/#categories 72 | 73 | author = ( 74 | # Fields relating to the author of this app. 75 | 76 | contactEmail = "jan@jambor.pro", 77 | # Email address to contact for any issues with this app. This includes end-user support 78 | # requests as well as app store administrator requests, so it is very important that this be a 79 | # valid address with someone paying attention to it. 80 | 81 | pgpSignature = embed "pgp-signature", 82 | # PGP signature attesting responsibility for the app ID. This is a binary-format detached 83 | # signature of the following ASCII message (not including the quotes, no newlines, and 84 | # replacing with the standard base-32 text format of the app's ID): 85 | # 86 | # "I am the author of the Sandstorm.io app with the following ID: " 87 | # 88 | # You can create a signature file using `gpg` like so: 89 | # 90 | # echo -n "I am the author of the Sandstorm.io app with the following ID: " | gpg --sign > pgp-signature 91 | # 92 | # Further details including how to set up GPG and how to use keybase.io can be found 93 | # at https://docs.sandstorm.io/en/latest/developing/publishing-apps/#verify-your-identity 94 | 95 | upstreamAuthor = "Twostairs", 96 | # Name of the original primary author of this app, if it is different from the person who 97 | # produced the Sandstorm package. Setting this implies that the author connected to the PGP 98 | # signature only "packaged" the app for Sandstorm, rather than developing the app. 99 | # Remove this line if you consider yourself as the author of the app. 100 | ), 101 | pgpKeyring = embed "pgp-keyring", 102 | # A keyring in GPG keyring format containing all public keys needed to verify PGP signatures in 103 | # this manifest (as of this writing, there is only one: `author.pgpSignature`). 104 | # 105 | # To generate a keyring containing just your public key, do: 106 | # 107 | # gpg --export > keyring 108 | # 109 | # Where `` is a PGP key ID or email address associated with the key. 110 | 111 | description = (defaultText = embed "description.md"), 112 | # The app's description in Github-flavored Markdown format, to be displayed e.g. 113 | # in an app store. Note that the Markdown is not permitted to contain HTML nor image tags (but 114 | # you can include a list of screenshots separately). 115 | 116 | shortDescription = (defaultText = "Notetaking"), 117 | # A very short (one-to-three words) description of what the app does. For example, 118 | # "Document editor", or "Notetaking", or "Email client". This will be displayed under the app 119 | # title in the grid view in the app market. 120 | 121 | screenshots = [ 122 | # Screenshots to use for marketing purposes. Examples below. 123 | # Sizes are given in device-independent pixels, so if you took these 124 | # screenshots on a Retina-style high DPI screen, divide each dimension by two. 125 | 126 | (width = 1166, height = 675, png = embed "app-graphics/paperwork_example_01.png"), 127 | (width = 1166, height = 675, png = embed "app-graphics/paperwork_example_02.png"), 128 | (width = 1166, height = 675, png = embed "app-graphics/paperwork_example_03.png") 129 | ], 130 | changeLog = (defaultText = embed "CHANGELOG.md"), 131 | # Documents the history of changes in Github-flavored markdown format (with the same restrictions 132 | # as govern `description`). We recommend formatting this with an H1 heading for each version 133 | # followed by a bullet list of changes. 134 | ), 135 | ), 136 | 137 | sourceMap = ( 138 | # Here we defined where to look for files to copy into your package. The 139 | # `spk dev` command actually figures out what files your app needs 140 | # automatically by running it on a FUSE filesystem. So, the mappings 141 | # here are only to tell it where to find files that the app wants. 142 | searchPath = [ 143 | ( sourcePath = "." ), # Search this directory first. 144 | ( sourcePath = "/", # Then search the system root directory. 145 | hidePaths = [ "home", "proc", "sys", 146 | "etc/passwd", "etc/hosts", "etc/host.conf", 147 | "etc/nsswitch.conf", "etc/resolv.conf" ] 148 | # You probably don't want the app pulling files from these places, 149 | # so we hide them. Note that /dev, /var, and /tmp are implicitly 150 | # hidden because Sandstorm itself provides them. 151 | ) 152 | ] 153 | ), 154 | 155 | fileList = "sandstorm-files.list", 156 | # `spk dev` will write a list of all the files your app uses to this file. 157 | # You should review it later, before shipping your app. 158 | 159 | alwaysInclude = [], 160 | # Fill this list with more names of files or directories that should be 161 | # included in your package, even if not listed in sandstorm-files.list. 162 | # Use this to force-include stuff that you know you need but which may 163 | # not have been detected as a dependency during `spk dev`. If you list 164 | # a directory here, its entire contents will be included recursively. 165 | 166 | bridgeConfig = ( 167 | # Used for integrating permissions and roles into the Sandstorm shell 168 | # and for sandstorm-http-bridge to pass to your app. 169 | # Uncomment this block and adjust the permissions and roles to make 170 | # sense for your app. 171 | # For more information, see high-level documentation at 172 | # https://docs.sandstorm.io/en/latest/developing/auth/ 173 | # and advanced details in the "BridgeConfig" section of 174 | # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/package.capnp 175 | viewInfo = ( 176 | # For details on the viewInfo field, consult "ViewInfo" in 177 | # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/grain.capnp 178 | permissions = [ 179 | # Permissions which a user may or may not possess. A user's current 180 | # permissions are passed to the app as a comma-separated list of `name` 181 | # fields in the X-Sandstorm-Permissions header with each request. 182 | # 183 | # IMPORTANT: only ever append to this list! Reordering or removing fields 184 | # will change behavior and permissions for existing grains! To deprecate a 185 | # permission, or for more information, see "PermissionDef" in 186 | # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/grain.capnp 187 | ( 188 | name = "admin", 189 | # Name of the permission, used as an identifier for the permission in cases where string 190 | # names are preferred. Used in sandstorm-http-bridge's X-Sandstorm-Permissions HTTP header. 191 | 192 | title = (defaultText = "admin"), 193 | # Display name of the permission, e.g. to display in a checklist of permissions 194 | # that may be assigned when sharing. 195 | 196 | description = (defaultText = "Is the owner.") 197 | # Prose describing what this role means, suitable for a tool tip or similar help text. 198 | ), 199 | ( 200 | name = "write", 201 | title = (defaultText = "write"), 202 | description = (defaultText = "Can write.") 203 | ), 204 | ( 205 | name = "read", 206 | title = (defaultText = "read"), 207 | description = (defaultText = "Can read.") 208 | ) 209 | ], 210 | roles = [ 211 | # Roles are logical collections of permissions. For instance, your app may have 212 | # a "viewer" role and an "editor" role 213 | ( 214 | title = (defaultText = "Owner"), 215 | # Name of the role. Shown in the Sandstorm UI to indicate which users have which roles. 216 | 217 | permissions = [true, true, true], 218 | # An array indicating which permissions this role carries. 219 | # It should be the same length as the permissions array in 220 | # viewInfo, and the order of the lists must match. 221 | 222 | verbPhrase = (defaultText = "Is owner, can add & edit notes."), 223 | # Brief explanatory text to show in the sharing UI indicating 224 | # what a user assigned this role will be able to do with the grain. 225 | 226 | description = (defaultText = "Owners may view and edit all notes and change settings."), 227 | # Prose describing what this role means, suitable for a tool tip or similar help text. 228 | ), 229 | ( 230 | title = (defaultText = "Read & write"), 231 | permissions = [false, true, true], 232 | verbPhrase = (defaultText = "Can add & edit notes."), 233 | description = (defaultText = "User may view and edit all notes.") 234 | ), 235 | ( 236 | title = (defaultText = "Read only"), 237 | permissions = [false, false, true], 238 | verbPhrase = (defaultText = "Can read existing notes."), 239 | description = (defaultText = "Users may view all notes.") 240 | ) 241 | ] 242 | ) 243 | # Apps can export an API to the world. The API is to be used primarily by Javascript 244 | # code and native apps, so it can't serve out regular HTML to browsers. If a request 245 | # comes in to your app's API, sandstorm-http-bridge will prefix the request's path with 246 | # this string, if specified. 247 | ) 248 | ); 249 | 250 | const myCommand :Spk.Manifest.Command = ( 251 | # Here we define the command used to start up your server. 252 | argv = ["/sandstorm-http-bridge", "8000", "--", "/opt/app/.sandstorm/launcher.sh"], 253 | environ = [ 254 | # Note that this defines the *entire* environment seen by your app. 255 | (key = "PATH", value = "/usr/local/bin:/usr/bin:/bin"), 256 | (key = "SANDSTORM", value = "1"), 257 | # Export SANDSTORM=1 into the environment, so that apps running within Sandstorm 258 | # can detect if $SANDSTORM="1" at runtime, switching UI and/or backend to use 259 | # the app's Sandstorm-specific integration code. 260 | (key = "HOME", value = "/var") 261 | ] 262 | ); 263 | -------------------------------------------------------------------------------- /.sandstorm/sandstorm-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamborJan/paperwork/14d35b48aad2171e54fdb95c9bddcc841d1eae82/.sandstorm/sandstorm-screenshot.png -------------------------------------------------------------------------------- /.sandstorm/service-config/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /.sandstorm/service-config/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 4; 2 | pid /var/run/nginx.pid; 3 | 4 | events { 5 | worker_connections 768; 6 | # multi_accept on; 7 | } 8 | 9 | http { 10 | # Basic Settings 11 | sendfile on; 12 | tcp_nopush on; 13 | tcp_nodelay on; 14 | keepalive_timeout 65; 15 | types_hash_max_size 2048; 16 | # server_names_hash_bucket_size 64; 17 | server_tokens off; 18 | server_name_in_redirect off; 19 | 20 | include mime.types; 21 | default_type application/octet-stream; 22 | 23 | # Logging 24 | access_log off; 25 | error_log stderr; 26 | 27 | # Prevent nginx from adding compression; this interacts badly with Sandstorm 28 | # WebSession due to https://github.com/sandstorm-io/sandstorm/issues/289 29 | gzip off; 30 | 31 | # Trust the sandstorm-http-bridge's X-Forwarded-Proto. 32 | map $http_x_forwarded_proto $fe_https { 33 | default ""; 34 | https on; 35 | } 36 | 37 | server { 38 | listen 8000 default_server; 39 | listen [::]:8000 default_server ipv6only=on; 40 | 41 | # Allow arbitrarily large bodies - Sandstorm can handle them, and requests 42 | # are authenticated already, so there's no reason for apps to add additional 43 | # limits by default. 44 | client_max_body_size 0; 45 | 46 | server_name localhost; 47 | root /opt/app/paperwork/frontend/public; 48 | 49 | location / { 50 | index index.php; 51 | try_files $uri $uri/ /index.php?$query_string; 52 | } 53 | location ~ \.php$ { 54 | fastcgi_pass unix:/var/run/php5-fpm.sock; 55 | fastcgi_index index.php; 56 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 57 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 58 | fastcgi_param QUERY_STRING $query_string; 59 | fastcgi_param REQUEST_METHOD $request_method; 60 | fastcgi_param CONTENT_TYPE $content_type; 61 | fastcgi_param CONTENT_LENGTH $content_length; 62 | 63 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 64 | fastcgi_param REQUEST_URI $request_uri; 65 | fastcgi_param DOCUMENT_URI $document_uri; 66 | fastcgi_param DOCUMENT_ROOT $document_root; 67 | fastcgi_param SERVER_PROTOCOL $server_protocol; 68 | fastcgi_param HTTPS $fe_https if_not_empty; 69 | 70 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 71 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 72 | 73 | fastcgi_param REMOTE_ADDR $remote_addr; 74 | fastcgi_param REMOTE_PORT $remote_port; 75 | fastcgi_param SERVER_ADDR $server_addr; 76 | fastcgi_param SERVER_PORT $server_port; 77 | fastcgi_param SERVER_NAME $server_name; 78 | 79 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 80 | fastcgi_param REDIRECT_STATUS 200; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /.sandstorm/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | export DEBIAN_FRONTEND=noninteractive 6 | # Add latest nodejs sources 7 | curl -sL https://deb.nodesource.com/setup_7.x | bash - 8 | apt-get update 9 | apt-get install -y nginx php5-fpm php5-mysql php5-cli php5-dev php5-gd php5-mcrypt php5-ldap mysql-server nodejs git 10 | # Install bower and gulp 11 | npm install -g gulp bower 12 | # Enable mcrypt for PHP 13 | php5enmod mcrypt 14 | # Stop and disable services 15 | service nginx stop 16 | service php5-fpm stop 17 | service mysql stop 18 | systemctl disable nginx 19 | systemctl disable php5-fpm 20 | systemctl disable mysql 21 | # patch /etc/php5/fpm/pool.d/www.conf to not change uid/gid to www-data 22 | sed --in-place='' \ 23 | --expression='s/^listen.owner = www-data/;listen.owner = www-data/' \ 24 | --expression='s/^listen.group = www-data/;listen.group = www-data/' \ 25 | --expression='s/^user = www-data/;user = www-data/' \ 26 | --expression='s/^group = www-data/;group = www-data/' \ 27 | /etc/php5/fpm/pool.d/www.conf 28 | # patch /etc/php5/fpm/php-fpm.conf to not have a pidfile 29 | sed --in-place='' \ 30 | --expression='s/^pid =/;pid =/' \ 31 | /etc/php5/fpm/php-fpm.conf 32 | # patch /etc/php5/fpm/pool.d/www.conf to no clear environment variables 33 | # so we can pass in SANDSTORM=1 to apps 34 | sed --in-place='' \ 35 | --expression='s/^;clear_env = no/clear_env=no/' \ 36 | /etc/php5/fpm/pool.d/www.conf 37 | # patch /etc/php5/fpm/php.ini to allow backup uploads up to 100MB in Paperwork 38 | sed --in-place='' \ 39 | --expression='s/^post_max_size =.*/post_max_size = 20M/' \ 40 | --expression='s/^upload_max_filesize =.*/upload_max_filesize = 20M/' \ 41 | --expression='s/^max_execution_time =.*/max_execution_time = 300/' \ 42 | --expression='s/^max_input_time =.*/max_input_time = 360/' \ 43 | /etc/php5/fpm/php.ini 44 | # patch mysql conf to not change uid, and to use /var/tmp over /tmp 45 | # see https://github.com/sandstorm-io/vagrant-spk/issues/195 46 | sed --in-place='' \ 47 | --expression='s/^user\t\t= mysql/#user\t\t= mysql/' \ 48 | --expression='s,^tmpdir\t\t= /tmp,tmpdir\t\t= /var/tmp,' \ 49 | --expression='/\[mysqld]/ a\ secure-file-priv = ""\' \ 50 | /etc/mysql/my.cnf 51 | # patch mysql conf to use smaller transaction logs to save disk space 52 | cat < /etc/mysql/conf.d/sandstorm.cnf 53 | [mysqld] 54 | # Set the transaction log file to the minimum allowed size to save disk space. 55 | innodb_log_file_size = 1048576 56 | # Set the main data file to grow by 1MB at a time, rather than 8MB at a time. 57 | innodb_autoextend_increment = 1 58 | EOF 59 | 60 | # Go to app directory 61 | cd /opt/app/ 62 | # Purge git repository just in case it is there 63 | rm -rf /opt/app/paperwork 64 | 65 | # Clone git repository for master build 66 | git clone https://github.com/twostairs/paperwork 67 | 68 | # Clone other repos & checkout branches for tests 69 | #git clone https://github.com/Liongold/paperwork 70 | #cd /opt/app/paperwork/ 71 | #git checkout branch-name 72 | 73 | exit 0 74 | -------------------------------------------------------------------------------- /.sandstorm/stack: -------------------------------------------------------------------------------- 1 | lemp 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 twostairs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project is dormant ([reasons](https://gist.github.com/JamborJan/8e42eff813b6fb0b6fe987afb9241c5e)). 2 | 3 | Issues have been closed as there will be no further progress on them. Feel free to fork the repo and take over maintenance. 4 | 5 | # Paperwork Sandstorm package 6 | 7 | This is the Sandstorm package of [Paperwork](http://paperwork.rocks/). 8 | 9 | ![Paperwork main view](.sandstorm/app-graphics/paperwork_example_01.png) 10 | 11 | # How to 12 | 13 | The package is done with [vagrant-spk](https://github.com/sandstorm-io/vagrant-spk), a tool designed to help app developers package apps for [Sandstorm](https://sandstorm.io). 14 | 15 | You can follow the following steps to make your own package or to contribute. 16 | 17 | ## Prerequisites 18 | 19 | You will need to install: 20 | - [Vagrant](https://www.vagrantup.com/) 21 | - [VirtualBox](https://www.virtualbox.org/wiki/Downloads) 22 | - Git 23 | 24 | ## Step by Step 25 | 26 | ``` 27 | git clone https://github.com/sandstorm-io/vagrant-spk 28 | git clone https://github.com/JamborJan/paperwork 29 | export PATH=$(pwd)/vagrant-spk:$PATH 30 | cd paperwork 31 | vagrant-spk vm up 32 | vagrant-spk dev 33 | ``` 34 | 35 | visit [http://local.sandstorm.io:6080/](http://local.sandstorm.io:6080/) in a web browser 36 | 37 | Note: when you want to fork this repo and create actual app packages for the app store you would need either the original app key or create a new one and make your own app. 38 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/config/paperwork.php: -------------------------------------------------------------------------------- 1 | array( 31 | 'external' => array( 32 | 'dns' => array_key_exists('HTTP_X_SANDSTORM_BASE_PATH', $_SERVER) ? $_SERVER[ 'HTTP_X_SANDSTORM_BASE_PATH'] : 'localhost', 33 | 'ports' => array( 34 | 'http' => 8000, 35 | 'https' => 8000, 36 | 'forceHttps' => true 37 | ) 38 | ), 39 | // if same as external: 40 | // 'internal' => null 41 | 'internal' => array( 42 | 'dns' => 'localhost', 43 | 'ports' => array( 44 | 'http' => 8888, 45 | 'https' => 8443, 46 | 'forceHttps' => false 47 | ) 48 | ) 49 | ), 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | Sandstorm.io mode 54 | |-------------------------------------------------------------------------- 55 | | 56 | | Sandstorm.io allows users to host their own servers and install apps 57 | | like on a mobile phone via an appstore. If you enable this option you 58 | | prepare the system for building a spk package for Sandstorm. 59 | | 60 | | The debug option shows Sandstorm permissions in the left sidebar 61 | | 62 | */ 63 | 'sandstorm' => true, 64 | 'sandstorm_debug' => false, 65 | 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | Enable registration 69 | |-------------------------------------------------------------------------- 70 | | 71 | | If set to true, user registration is enabled. If set to false 72 | | no new users will be able to register. 73 | | 74 | */ 75 | 'registration' => isset($configuration->registration) ? ($configuration->registration == "true") : true, 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Enable "forgot password" link 80 | |-------------------------------------------------------------------------- 81 | | 82 | | If set to true, forgot password link is enabled. 83 | | 84 | */ 85 | 'forgot_password' => isset($configuration->forgot_password) ? ($configuration->forgot_password == "true") : true, 86 | 87 | /* 88 | |-------------------------------------------------------------------------- 89 | | Automatically detect language through user agent 90 | |-------------------------------------------------------------------------- 91 | | 92 | | If set to true, Paperwork will automatically set the user-interface 93 | | language depending on the user-agent languages. If set to false, the 94 | | language set in app.php -> locale will be used. 95 | | 96 | */ 97 | 'userAgentLanguage' => isset($configuration->userAgentLanguage) ? ($configuration->userAgentLanguage == "true") : false, 98 | 99 | /* 100 | |-------------------------------------------------------------------------- 101 | | Set which characters are allowed in user first and last names 102 | |-------------------------------------------------------------------------- 103 | | 104 | | By default, alpha, hyphen, apostrophe, and space are allowed 105 | | 106 | */ 107 | 108 | 'nameCharactersAllowed' => array( 109 | //Alphabetic characters 110 | 'alpha' => true, 111 | //- 112 | 'hyphen' => true, 113 | // 0-9 114 | 'num' => false, 115 | //_ 116 | 'underscore' => false, 117 | //' 118 | 'apostrophe' => true, 119 | //" " Note, leading and trailing spaces are still trimmed 120 | 'space' => true, 121 | ), 122 | 123 | /* 124 | |-------------------------------------------------------------------------- 125 | | Directory where uploaded files are being saved in 126 | |-------------------------------------------------------------------------- 127 | | 128 | | The directory for saving uploaded files in. 129 | | 130 | */ 131 | 'attachmentsDirectory' => storage_path() . '/attachments', 132 | 133 | /* 134 | |-------------------------------------------------------------------------- 135 | | Attachments preview settings 136 | |-------------------------------------------------------------------------- 137 | | 138 | | Settings regarding attachments preview generation. 139 | | 140 | | 'resolution' defines the generated resolution. The higher this is, the 141 | | more disk space previews will consume. 142 | | 143 | | 'directory' defines the directory in which to store the previews. 144 | | This usually is the same as 'attachmentsDirectory'. 145 | | 146 | */ 147 | 'attachmentsPreview' => array( 148 | 'resolution' => array( 149 | 'x' => 500, 150 | 'y' => 500 151 | ), 152 | 'directory' => storage_path() . '/attachments' 153 | ), 154 | 155 | /* 156 | |-------------------------------------------------------------------------- 157 | | Tesseract temporary directory 158 | |-------------------------------------------------------------------------- 159 | | 160 | | The directory where temporary tesseract files are being store into. 161 | | 162 | */ 163 | 'tesseractTempDirectory' => storage_path() . '/cache', 164 | 165 | /* 166 | |-------------------------------------------------------------------------- 167 | | Show issue reporting link 168 | |-------------------------------------------------------------------------- 169 | | 170 | | If set to true, a link for reporting issues is being displayed. 171 | | 172 | */ 173 | 'showIssueReportingLink' => isset($configuration->showIssueReportingLink) ? ($configuration->showIssueReportingLink == "true") : true, 174 | 175 | /* 176 | |-------------------------------------------------------------------------- 177 | | Prefix character that describes a public tag 178 | |-------------------------------------------------------------------------- 179 | | 180 | | The prefix character that describes a public tag. Example: 181 | | When a user adds a new tag (e.g. "meeting") without this character, the 182 | | tag is only visible to him, even when the note is being shared with 183 | | other users. 184 | | If the user prefixes the tag with this character (e.g. "+meeting"), this 185 | | tag will be visible to him and others he shared the note with. 186 | | 187 | | The maximum accepted length of this variable is exactly one character. 188 | | 189 | | WARNING: This value may only be changed during setup, as long as no 190 | | tags have been added. If you change this afterwards, things will 191 | | break. 192 | | 193 | */ 194 | 'tagsPublicPrefixCharacter' => '+', 195 | 'purgeTagList' => ['script'], 196 | 197 | /* 198 | |------------------------------------------------------------------------ 199 | | Maximum Attachments Per Note 200 | |------------------------------------------------------------------------ 201 | | 202 | | The maximum number of attachments that can be attachned to each note. 203 | */ 204 | 'maximumAttachmentsPerNote' => 10, 205 | 206 | ); 207 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/controllers/UserController.php: -------------------------------------------------------------------------------- 1 | userRegistrator = $userRegistrator; 22 | $this->isLdap = PaperworkHelpers::isLdap(); 23 | } 24 | 25 | public function showRegistrationForm() 26 | { 27 | if(Auth::user()) { 28 | $admin = User::find(Auth::user()->id)->is_admin; 29 | }else{ 30 | $admin = false; 31 | } 32 | 33 | return View::make('user.register')->with('admin', $admin); 34 | } 35 | 36 | /** 37 | * Register action. 38 | * 39 | * @return $this|\Illuminate\Http\RedirectResponse 40 | */ 41 | public function register() 42 | { 43 | $validator = $this->getRegistrationValidator(); 44 | 45 | if ($validator->passes()) { 46 | //only allow users to register who actually have a valid ldap account 47 | if ($this->isLdap) { 48 | $creds = $this->getLoginCredentials(); 49 | $creds['isRegister'] = true; 50 | if (!Auth::validate($creds)) { 51 | return Redirect::back() 52 | ->withInput() 53 | ->withErrors(["password" => [Lang::get('messages.invalid_credentials')]]); 54 | } 55 | } 56 | //if we are using ldap and auto registration, the user will have been created in the Auth::attemp call above 57 | //thus, we need to just load the user using eloquent and not create a new one. 58 | if ($this->isLdap && Config::get('ldap.autoRegister')) { 59 | $user = User::query() 60 | ->where('username', Input::get('username')) 61 | ->first(); 62 | } else { 63 | $user = 64 | $this->userRegistrator->registerUser(Input::except('_token', 65 | 'password_confirmation', 'ui_language', 'admin_creator'), 66 | Input::get('ui_language')); 67 | } 68 | 69 | if(Input::get('admin_creator') == true) { 70 | return Redirect::route("admin/console"); 71 | }else if ($user && !Request::ajax()) { 72 | Auth::login($user); 73 | 74 | Session::put('ui_language', Input::get('ui_language')); 75 | 76 | return Redirect::route("/"); 77 | }else if($user) { 78 | return PaperworkHelpers::apiResponse(PaperworkHelpers::STATUS_SUCCESS, array()); 79 | } 80 | 81 | if(!Request::ajax()) { 82 | return Redirect::back() 83 | ->withErrors(["password" => [Lang::get('messages.account_creation_failed')]]); 84 | }else{ 85 | return Response::json(array('html' => View::make('partials/registration-form', array('password' => Lang::get('messages.account_creation_failed'))), 'input' => Input::all()), 400); 86 | 87 | } 88 | } else { 89 | if(!Request::ajax()) { 90 | return Redirect::back()->withInput()->withErrors($validator); 91 | }else{ 92 | return Response::json(array('html' => View::make('partials/registration-form')->withErrors($validator)->render(), 'input' => Input::all()), 400); 93 | } 94 | } 95 | } 96 | 97 | public function login() 98 | { 99 | $validator = $this->getLoginValidator(); 100 | 101 | if ($validator->passes()) { 102 | $credentials = $this->getLoginCredentials(); 103 | 104 | if (Auth::attempt($credentials, Input::has('remember_me'))) { 105 | $settings = 106 | Setting::where('user_id', '=', Auth::user()->id)->first(); 107 | 108 | Session::put('ui_language', $settings->ui_language); 109 | 110 | return Redirect::route("/"); 111 | } 112 | 113 | return Redirect::back() 114 | ->withErrors(["password" => [Lang::get('messages.invalid_credentials')]]); 115 | 116 | } else { 117 | return Redirect::back()->withInput()->withErrors($validator); 118 | } 119 | } 120 | 121 | public function showLoginForm() 122 | { 123 | return View::make('user/login'); 124 | } 125 | 126 | public function checkSandstormUsers() 127 | { 128 | 129 | // get permission via HTTP_X_SANDSTORM header 130 | $sandstorm_permissions = $_SERVER[ 'HTTP_X_SANDSTORM_PERMISSIONS']; 131 | // Only when we are admin, we check and create users 132 | if ($sandstorm_permissions == "admin,write,read") { 133 | // check for admin user 134 | if (User::where('username', '=', 'sandstorm_admin')->count() == 0) { 135 | $sandstorm_admin = User::create(Input::except('_token', 'password_confirmation', 'ui_language')); 136 | if ($sandstorm_admin) { 137 | //make the first user an admin 138 | $sandstorm_admin->firstname = "sandstorm_admin"; 139 | $sandstorm_admin->lastname = " "; 140 | $sandstorm_admin->username = "sandstorm_admin"; 141 | $sandstorm_admin->password = "sandstorm_admin"; 142 | $sandstorm_admin->is_admin = 1; 143 | $sandstorm_admin->save(); 144 | $setting_sandstorm_admin = Setting::create(['ui_language' => 'en' , 'user_id' => $sandstorm_admin->id]); 145 | } 146 | } else { 147 | $sandstorm_admin = User::where('username', '=', 'sandstorm_admin'); 148 | } 149 | // Then the read & write user 150 | if (User::where('username', '=', 'sandstorm_readwrite')->count() == 0) { 151 | $sandstorm_readwrite = User::create(Input::except('_token', 'password_confirmation', 'ui_language')); 152 | if ($sandstorm_readwrite) { 153 | $sandstorm_readwrite->firstname = "sandstorm_readwrite"; 154 | $sandstorm_readwrite->lastname = " "; 155 | $sandstorm_readwrite->username = "sandstorm_readwrite"; 156 | $sandstorm_readwrite->password = "sandstorm_readwrite"; 157 | $sandstorm_readwrite->save(); 158 | $setting_sandstorm_readwrite = Setting::create(['ui_language' => 'en' , 'user_id' => $sandstorm_readwrite->id]); 159 | } 160 | } else { 161 | $sandstorm_readwrite = User::where('username', '=', 'sandstorm_readwrite'); 162 | } 163 | // Then the read only user 164 | if (User::where('username', '=', 'sandstorm_readonly')->count() == 0) { 165 | $sandstorm_readonly = User::create(Input::except('_token', 'password_confirmation', 'ui_language')); 166 | if ($sandstorm_readonly) { 167 | $sandstorm_readonly->firstname = "sandstorm_readonly"; 168 | $sandstorm_readonly->lastname = " "; 169 | $sandstorm_readonly->username = "sandstorm_readonly"; 170 | $sandstorm_readonly->password = "sandstorm_readonly"; 171 | $sandstorm_readonly->save(); 172 | $setting_sandstorm_readonly = Setting::create(['ui_language' => 'en' , 'user_id' => $sandstorm_readonly->id]); 173 | } 174 | } else { 175 | $sandstorm_readonly = User::where('username', '=', 'sandstorm_readonly'); 176 | } 177 | // Now that the required users are there we create the default 178 | if ((Notebook::all()->count() == 0) && (Tag::all()->count() == 0) && (Note::all()->count() == 0)) { 179 | // Notebook ... 180 | $notebookCreate = new Notebook(); 181 | $notebookCreate->title = Lang::get('notebooks.welcome_notebook_title'); 182 | $notebookCreate->save(); 183 | $notebookCreate->users()->attach($sandstorm_readonly->id, ['umask' => PaperworkHelpers::UMASK_READONLY]); 184 | $notebookCreate->users()->attach($sandstorm_readwrite->id, ['umask' => PaperworkHelpers::UMASK_READWRITE]); 185 | $notebookCreate->users()->attach($sandstorm_admin->id, ['umask' => PaperworkHelpers::UMASK_OWNER]); 186 | // Tag ... 187 | $tagCreate = new Tag(); 188 | $tagCreate->title = Lang::get('notebooks.welcome_note_tag'); 189 | $tagCreate->visibility = 1; 190 | $tagCreate->user_id=$sandstorm_admin->id; 191 | $tagCreate->save(); 192 | // Note ... 193 | $noteCreate = new Note; 194 | $versionCreate = new Version([ 195 | 'title' => Lang::get('notebooks.welcome_note_title'), 196 | 'content' => Lang::get('notebooks.welcome_note_content'), 197 | 'content_preview' => mb_substr(strip_tags(Lang::get('notebooks.welcome_note_content')), 0, 255), 198 | 'user_id' => $sandstorm_admin->id 199 | ]); 200 | $versionCreate->save(); 201 | $noteCreate->version()->associate($versionCreate); 202 | $noteCreate->notebook_id = $notebookCreate->id; 203 | $noteCreate->save(); 204 | $noteCreate->users()->attach($sandstorm_readonly->id, ['umask' => PaperworkHelpers::UMASK_READONLY]); 205 | $noteCreate->users()->attach($sandstorm_readwrite->id, ['umask' => PaperworkHelpers::UMASK_READWRITE]); 206 | $noteCreate->users()->attach($sandstorm_admin->id, ['umask' => PaperworkHelpers::UMASK_OWNER]); 207 | $noteCreate->tags()->sync([$tagCreate->id]); 208 | } 209 | } 210 | 211 | // login 212 | if ($sandstorm_permissions == "read") { 213 | $credentials = ["username" => "sandstorm_readonly", "password" => "sandstorm_readonly"]; 214 | } 215 | if ($sandstorm_permissions == "write,read") { 216 | $credentials = ["username" => "sandstorm_readwrite", "password" => "sandstorm_readwrite"]; 217 | } 218 | if ($sandstorm_permissions == "admin,write,read") { 219 | $credentials = ["username" => "sandstorm_admin", "password" => "sandstorm_admin"]; 220 | } 221 | if (Auth::attempt($credentials)) { 222 | $settings = Setting::where('user_id', '=', Auth::user()->id)->first(); 223 | Session::put('ui_language', $settings->ui_language); 224 | return Redirect::route("/"); 225 | } 226 | 227 | } 228 | 229 | protected function isPostRequest() 230 | { 231 | return Input::server("REQUEST_METHOD") == "POST"; 232 | } 233 | 234 | protected function getRegistrationValidator() 235 | { 236 | $attributes = ["username" => "email address"]; 237 | $validator = Validator::make(Input::all(), [ 238 | "username" => $this->isLdap ? "required|unique:users" : 239 | "required|email|unique:users", 240 | "password" => "required|min:5|confirmed", 241 | "password_confirmation" => "required", 242 | "firstname" => "required|name_validator", 243 | "lastname" => "required|name_validator" 244 | ]); 245 | 246 | $validator->setAttributeNames($attributes); 247 | 248 | return $validator; 249 | } 250 | 251 | protected function getLoginValidator() 252 | { 253 | return Validator::make(Input::all(), [ 254 | "username" => $this->isLdap ? "required" : "required|email", 255 | "password" => "required" 256 | ]); 257 | } 258 | 259 | protected function getProfileValidator() 260 | { 261 | return Validator::make(Input::all(), [ 262 | "password" => "min:5|confirmed", 263 | "firstname" => "required|name_validator", 264 | "lastname" => "required|name_validator" 265 | ]); 266 | } 267 | 268 | protected function getSettingsValidator() 269 | { 270 | return Validator::make(Input::all(), ["ui_language" => "required"]); 271 | } 272 | 273 | protected function getLoginCredentials() 274 | { 275 | return [ 276 | "username" => Input::get("username"), 277 | "password" => Input::get("password") 278 | ]; 279 | } 280 | 281 | public function profile() 282 | { 283 | $user = User::find(Auth::user()->id); 284 | 285 | if ($this->isPostRequest()) { 286 | $validator = $this->getProfileValidator(); 287 | 288 | if ($validator->passes()) { 289 | $user->firstname = Input::get('firstname'); 290 | $user->lastname = Input::get('lastname'); 291 | 292 | $passwd = Input::get('password'); 293 | 294 | if (!is_null($passwd) && trim($passwd) != "") { 295 | $user->password = Input::get('password'); 296 | } 297 | 298 | if (!$user->save()) { 299 | return Redirect::back() 300 | ->withErrors(["password" => [Lang::get('messages.account_update_failed')]]); 301 | } 302 | } else { 303 | return Redirect::back()->withInput()->withErrors($validator); 304 | } 305 | } 306 | 307 | return View::make("user/profile")->with('user', $user); 308 | } 309 | 310 | public function settings() 311 | { 312 | $user = User::find(Auth::user()->id); 313 | $settings = Setting::where('user_id', '=', $user->id)->first(); 314 | 315 | if ($this->isPostRequest()) { 316 | $validator = $this->getSettingsValidator(); 317 | 318 | if ($validator->passes()) { 319 | $settings->ui_language = Input::get('ui_language'); 320 | $document_languages = Input::get('document_languages'); 321 | 322 | // TODO: I think this whole thing could be done nicer... 323 | DB::Table('language_user') 324 | ->where('user_id', '=', $user->id) 325 | ->delete(); 326 | 327 | if (!is_null($document_languages)) { 328 | foreach ($document_languages as $document_lang) { 329 | $foundLanguage = 330 | Language::where('language_code', '=', $document_lang) 331 | ->first(); 332 | if (!is_null($foundLanguage)) { 333 | $user->languages()->save($foundLanguage); 334 | } 335 | } 336 | } 337 | 338 | Session::put('ui_language', $settings->ui_language); 339 | App::setLocale($settings->ui_language); 340 | } else { 341 | return Redirect::back()->withInput()->withErrors($validator); 342 | } 343 | } 344 | 345 | $languages = []; 346 | $userDocumentLanguages = $user->languages()->get(); 347 | foreach ($userDocumentLanguages as $userDocumentLanguage) { 348 | $languages[$userDocumentLanguage->language_code] = true; 349 | } 350 | 351 | return View::make("user/settings") 352 | ->with('settings', $settings) 353 | ->with('languages', $languages); 354 | 355 | // TODO: 356 | // Think about whether we need to run an OCRing process in background, if document languages selection changed. 357 | } 358 | 359 | public function request() 360 | { 361 | if (Config::get('paperwork.forgot_password')) { 362 | if ($this->isPostRequest()) { 363 | $response = $this->getPasswordRemindResponse(); 364 | if ($this->isInvalidUser($response)) { 365 | return Redirect::back() 366 | ->withInput() 367 | ->with("error", Lang::get($response)); 368 | } 369 | 370 | return Redirect::back()->with("status", Lang::get($response)); 371 | } 372 | 373 | return View::make("user/request"); 374 | } else { 375 | return View::make("404"); 376 | } 377 | } 378 | 379 | // TODO: Password reminders not working out of the box, since we don't have an "email" column. 380 | protected function getPasswordRemindResponse() 381 | { 382 | return Password::remind(Input::only("username"), function ($message) { 383 | $message->subject(Lang::get('keywords.password_reset_request')); 384 | }); 385 | } 386 | 387 | protected function isInvalidUser($response) 388 | { 389 | return $response === Password::INVALID_USER; 390 | } 391 | 392 | public function reset($token) 393 | { 394 | if ($this->isPostRequest()) { 395 | $credentials = 396 | Input::only("username", "password", "password_confirmation") + 397 | compact("token"); 398 | $response = $this->resetPassword($credentials); 399 | if ($response === Password::PASSWORD_RESET) { 400 | return Redirect::route("user/profile"); 401 | } 402 | 403 | return Redirect::back() 404 | ->withInput() 405 | ->with("error", Lang::get($response)); 406 | } 407 | 408 | return View::make("user/reset", compact("token")); 409 | } 410 | 411 | protected function resetPassword($credentials) 412 | { 413 | return Password::reset($credentials, function ($user, $pass) { 414 | $user->password = Hash::make($pass); 415 | $user->save(); 416 | }); 417 | } 418 | 419 | public function help($topic = "index") 420 | { 421 | if ($topic[0] == '.') { 422 | $topic = "index"; 423 | } 424 | 425 | $topic_path = $this->helpGenerateTopicPath($topic); 426 | 427 | if (is_null($topic_path)) { 428 | return View::make('404'); 429 | } 430 | 431 | $topic_content = $this->helpGetAndPrepareContent($topic_path); 432 | 433 | return View::make("user/help")->with(['topic' => $topic_content]); 434 | } 435 | 436 | private function helpGenerateTopicPath($topic) 437 | { 438 | $topic_clean = str_replace('.', '/', $topic); 439 | 440 | $topic_path = 441 | app_path() . '/help/' . $topic_clean . '.' . App::getLocale() . '.md'; 442 | $topic_path_alternative = 443 | app_path() . '/help/' . $topic_clean . '/index.' . App::getLocale() . 444 | '.md'; 445 | 446 | if (File::exists($topic_path)) { 447 | return $topic_path; 448 | } elseif (File::exists($topic_path_alternative)) { 449 | return $topic_path_alternative; 450 | } else { 451 | return null; 452 | } 453 | 454 | } 455 | 456 | private function helpGetAndPrepareContent($path) 457 | { 458 | $content = File::get($path); 459 | 460 | $conent_prepared = 461 | preg_replace_callback('/(\@(help|image))((.[A-Za-z0-9]+)+)/', 462 | function ($match) { 463 | if (is_null($match) || count($match) < 4) { 464 | return $match; 465 | } 466 | $topic = ltrim($match[3], '.'); 467 | if ($match[1] === "@help") { 468 | return URL::route("user/help", $topic); 469 | } elseif ($match[1] === "@image") { 470 | return url('/images/help/' . $topic); 471 | } 472 | }, $content); 473 | 474 | $pdown = new Parsedown(); 475 | 476 | return $pdown->text($conent_prepared); 477 | } 478 | 479 | public function logout() 480 | { 481 | Auth::logout(); 482 | Session::flush(); 483 | 484 | return Redirect::route("user/login"); 485 | } 486 | 487 | public function import() 488 | { 489 | if ($this->isPostRequest()) { 490 | if(Input::hasFile('enex')) { 491 | $notebookNew = with(new \Paperwork\Import\EvernoteImport())->import(Input::file('enex')); 492 | if($notebookNew[0]) { 493 | return Redirect::route("user/settings") 494 | ->withErrors(["enex_file_success" => $notebookNew[1]]); 495 | } 496 | else { 497 | return Redirect::route("user/settings") 498 | ->withErrors(["enex_file" => $notebookNew[1]]); 499 | } 500 | } else { 501 | return Redirect::route("user/settings") 502 | ->withErrors(["enex_file" => "You must choose an ENEX file!"]); 503 | } 504 | } else { 505 | return Redirect::route("user/settings") 506 | ->withErrors(["enex_file" => "Nothing selected!"]); 507 | } 508 | } 509 | 510 | public function export() 511 | { 512 | $file_content = ""; 513 | $noteNumber = 0; 514 | 515 | $notes = DB::table('notes') 516 | ->join('note_user', function ($join) { 517 | $join->on('notes.id', '=', 'note_user.note_id') 518 | ->where('note_user.user_id', '=', Auth::user()->id) 519 | ->where('note_user.umask', '=', '7'); 520 | }) 521 | ->join('notebooks', function ($join) { 522 | $join->on('notes.notebook_id', '=', 'notebooks.id'); 523 | }) 524 | ->join('versions', function ($join) { 525 | $join->on('notes.version_id', '=', 'versions.id'); 526 | }) 527 | ->select('notes.id', 'notebooks.title as notebook_title', 528 | 'versions.id as version_id', 'versions.title', 529 | 'versions.content', 'notes.created_at', 530 | 'notes.updated_at') 531 | ->whereNull('notes.deleted_at') 532 | ->whereNull('notebooks.deleted_at') 533 | ->get(); 534 | 535 | $noteCount = count($notes); 536 | foreach ($notes as $note) { 537 | $noteNumber++; 538 | $versionId = $note->version_id; 539 | $noteid = $note->id; 540 | 541 | $noteArray = [ 542 | 'title' => $note->title, 543 | 'content' => $note->content, 544 | 'created' => date('omd', strtotime($note->created_at)) . 'T' . 545 | date('His', strtotime($note->created_at)) . 'Z', 546 | 'updated' => date('omd', strtotime($note->updated_at)) . 'T' . 547 | date('His', strtotime($note->updated_at)) . 'Z' 548 | ]; 549 | 550 | $attachments = DB::table('attachment_version') 551 | ->join('versions', 552 | function ($join) use (&$versionId) { 553 | $join->on('attachment_version.version_id', 554 | '=', 'versions.id') 555 | ->where('versions.id', '=', $versionId); 556 | }) 557 | ->join('attachments', function ($join) { 558 | $join->on('attachment_version.attachment_id', 559 | '=', 'attachments.id'); 560 | }) 561 | ->select('attachments.id', 'attachments.filename', 562 | 'attachments.mimetype') 563 | ->whereNull('attachments.deleted_at') 564 | ->get(); 565 | 566 | $tags = DB::table('tags') 567 | ->join('tag_note', function ($join) use (&$noteid) { 568 | $join->on('tags.id', '=', 'tag_note.tag_id') 569 | ->where('tag_note.note_id', '=', $noteid); 570 | }) 571 | ->select('tags.title') 572 | ->get(); 573 | 574 | foreach ($tags as $tag) { 575 | $noteArray['tags'][] = ['title' => $tag->title]; 576 | } 577 | 578 | $noteArray['firstname'] = Auth::user()->firstname; 579 | $noteArray['lastname'] = Auth::user()->lastname; 580 | 581 | foreach ($attachments as $attachment) { 582 | $attachments_directory = 583 | Config::get('paperwork.attachmentsDirectory'); 584 | $path = 585 | $attachments_directory . "/" . $attachment->id . "/" . 586 | $attachment->filename; 587 | $file_contents = File::get($path); 588 | $data = base64_encode($file_contents); 589 | 590 | $noteArray['attachments'][] = [ 591 | 'hash' => md5($file_contents), 592 | 'filename' => $attachment->filename, 593 | 'mimetype' => $attachment->mimetype, 594 | 'encoded' => $data 595 | ]; 596 | } 597 | 598 | if ($noteNumber == 1) { 599 | $noteArray['start'] = 1; 600 | } 601 | 602 | if ($noteNumber == $noteCount) { 603 | $noteArray['end'] = 1; 604 | } 605 | 606 | $file_content .= View::make('user/settings/export_file', $noteArray) 607 | ->render(); 608 | } 609 | 610 | $headers = [ 611 | "Content-Type" => "application/xml", 612 | "Content-Disposition" => "attachment; filename=\"export.enex\"" 613 | ]; 614 | 615 | return Response::make(rtrim($file_content, "\r\n"), 200, $headers); 616 | } 617 | } 618 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/lang/de/messages.php: -------------------------------------------------------------------------------- 1 | 'Could not create account.', 6 | 'account_update_failed' => 'Could not update account.', 7 | 'invalid_credentials' => 'Your credentials are invalid.', 8 | 'note_version_info' => 'You are previewing an older version of this note.', 9 | 'found_bug' => 'Found a bug? Submit it on GitHub!', 10 | 'new_version_available' => 'Found a bug? It seems like you are not running the latest version of Paperwork. Please consider updating before submitting an issue. ', 11 | 'error_version_check' => 'Found a bug? Paperwork cannot check whether your version is the latest. Please make sure you are running the latest version before reporting any issues. ', 12 | 'error_message' => 'Whooops!', 13 | 'onbeforeunload_info' => 'Data will be lost if you leave the page, are you sure?', 14 | 'user' => array( 15 | 'settings' => array( 16 | 'language_label' => 'Language', 17 | 'client_label' => 'Client', 18 | 'import_slash_export' => 'Import/Export', 19 | 'import_error' => 'Der Import war nicht erfolgreich. Die technische Fehlermeldung ist: ', 20 | 'import_success' => 'Die Notizen wurden in folgendes Notizbuch importiert: ', 21 | 'language' => array( 22 | 'ui_language' => 'Benutzeroberflächensprache', 23 | 'document_languages' => 'Dokumentensprachen', 24 | 'document_languages_description' => 'Die Sprachen, welche Sie hier auswählen, werden verwendet, um hochgeladene Anhänge zu analysieren und Ihnen damit eine Suche über den Inhalt dieser zu erlauben. Ein Anhang könnte beispielsweise ein Foto eines Dokuments sein, welches sie mit ihrem Smartphone aufgenommen haben. Wählen Sie die Sprachen aus, in welchen Ihre Dokumente üblicherweise geschrieben werden.', 25 | ), 26 | 'client' => array( 27 | 'qrcode' => 'QR Code', 28 | 'scan' => 'Scannen Sie diesen QR Code mit Ihrer mobilen App um eine Autokonfiguration Ihres Paperwork Accounts durchzuführen.' 29 | ), 30 | 'import' => array( 31 | 'evernotexml' => 'Import einer Evernote XML-Datei:', 32 | 'upload_evernotexml' => 'Laden Sie hier den Evernote XML Export hoch um Ihre Notizen aus Evernot in Paperwork zu importieren.' 33 | ), 34 | 'export' => array( 35 | 'evernotexml' => 'Export als Evernote XML-Datei:', 36 | 'download_evernotexml' => 'Laden Sie eine ENEX kompatible Evernote Datei herunter um Ihre Paperwork Notizen zu exportieren.' 37 | ) 38 | ) 39 | ) 40 | ); 41 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/lang/en/messages.php: -------------------------------------------------------------------------------- 1 | 'Could not create account.', 6 | 'account_update_failed' => 'Could not update account.', 7 | 'invalid_credentials' => 'Your credentials are invalid.', 8 | 'note_version_info' => 'You are previewing an older version of this note.', 9 | 'found_bug' => 'Found a bug? Submit it on GitHub!', 10 | 'new_version_available' => 'Found a bug? It seems like you are not running the latest version of Paperwork. Please consider updating before submitting an issue. ', 11 | 'error_version_check' => 'Found a bug? Paperwork cannot check whether your version is the latest. Please make sure you are running the latest version before reporting any issues. ', 12 | 'found_bug_newer_commit_installed' => 'Found a bug? It seems like you have done some changes to the Paperwork code. Before opening a new issue, please check if this issue is present in the official source code available in our Github repository. ', 13 | 'error_message' => 'Whooops!', 14 | 'onbeforeunload_info' => 'Data will be lost if you leave the page, are you sure?', 15 | 'user' => array( 16 | 'settings' => array( 17 | 'language_label' => 'Language', 18 | 'client_label' => 'Client', 19 | 'import_slash_export' => 'Import/Export', 20 | 'import_error' => 'The import failed. The technical error message is: ', 21 | 'import_success' => 'Your notes have been imported into the notebook: ', 22 | 'language' => array( 23 | 'ui_language' => 'User Interface Language', 24 | 'document_languages' => 'Document Languages', 25 | 'document_languages_description' => 'The languages you select here will be used for parsing text within attachments you upload, allowing you to search for the content of these. An attachment could be a photo of a document you took with your smartphone, for example. Select the languages these documents are usually written in.', 26 | ), 27 | 'client' => array( 28 | 'qrcode' => 'QR Code', 29 | 'scan' => 'Scan this QR code with your mobile app to auto-configure your Paperwork account.' 30 | ), 31 | 'import' => array( 32 | 'evernotexml' => 'Import an Evernote XML:', 33 | 'upload_evernotexml' => 'Upload your Evernote XML export here, to import your Evernote notes into Paperwork.' 34 | ), 35 | 'export' => array( 36 | 'evernotexml' => 'Export as Evernote XML:', 37 | 'download_evernotexml' => 'Download an ENEX file compatible with Evernote to move your notes from Paperwork. ' 38 | ) 39 | ) 40 | ) 41 | ); 42 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/lib/Paperwork/Import/EvernoteImport.php: -------------------------------------------------------------------------------- 1 | xml['note'])) { 22 | $this->createNotebook('Evernote'.date('omd').'T'.date('His').'Z'); 23 | 24 | // libxml returns single element instead of array if 1 note 25 | if (isset($this->xml['note']['content'])) { 26 | $this->xml['note'] = [$this->xml['note']]; 27 | } 28 | 29 | libxml_use_internal_errors(true); 30 | foreach ($this->xml['note'] as $note) { 31 | $this->createEvernoteNote($note, $this->getNoteContent($note)); 32 | } 33 | libxml_use_internal_errors(false); 34 | } 35 | } 36 | 37 | /** 38 | * Identify parser class for xml 39 | * Use @attributes property 40 | * 41 | * @return bool 42 | */ 43 | protected function isEvernote() 44 | { 45 | $rootHasAttributes = isset($this->xml['@attributes']); 46 | $isAppSet = isset($this->xml['@attributes']['application']); 47 | $isEvernote = preg_match('/evernote/i', $this->xml['@attributes']['application']); 48 | $isPaperwork = preg_match('/paperwork/i', $this->xml['@attributes']['application']); 49 | 50 | return $rootHasAttributes && $isAppSet && ($isEvernote || $isPaperwork); 51 | } 52 | 53 | /** 54 | * Try parse file by SimpleXml 55 | * 56 | * @param UploadedFile $file 57 | * 58 | * @return bool|int 59 | */ 60 | public function import(UploadedFile $file) 61 | { 62 | try { 63 | $xmlfile = file_get_contents($file->getRealPath()); 64 | $xmlfile = html_entity_decode($xmlfile); 65 | $this->xml = simplexml_load_string($xmlfile, 'SimpleXMLElement', 66 | LIBXML_PARSEHUGE | LIBXML_NOCDATA); 67 | $this->xml = json_decode(json_encode($this->xml), true); 68 | 69 | if ($this->xml && $this->isEvernote()) { 70 | $this->process(); 71 | 72 | return array (true, $this->notebook->title); 73 | } 74 | } catch (Exception $e) { 75 | return array (false, $e->getMessage()); 76 | } 77 | 78 | return array (false, 'Something went wrong.'); 79 | } 80 | 81 | 82 | /** 83 | * Fetch html from xml note 84 | * 85 | * @param $note 86 | * @return string 87 | */ 88 | protected function getNoteContent($note) 89 | { 90 | // Get content from xml. Use DOMDocument 91 | $doc = new \DOMDocument; 92 | $doc->loadHTML($note['content']); 93 | 94 | // Remove xml, doctype and body tags 95 | $body = new \DOMDocument(); 96 | $cloned = $doc->getElementsByTagName('body')->item(0)->cloneNode(true); 97 | $body->appendChild($body->importNode($cloned, true)); 98 | $res = str_replace(array('', '', ''), array('', '', ''), 99 | $body->saveHTML()); 100 | return mb_convert_encoding($res, 'UTF-8', 'HTML-ENTITIES'); 101 | } 102 | 103 | /** 104 | * Create note, tags and attachments 105 | * I expected that $xmlNote has 'created' and 'created' times 106 | * 107 | * @param $xmlNote 108 | * @param string $content 109 | */ 110 | protected function createEvernoteNote($xmlNote, $content) 111 | { 112 | $noteInstance = $this->createNote($xmlNote['title'], $content, strtotime($xmlNote['created']), 113 | (isset($xmlNote['updated'])) ? strtotime($xmlNote['updated']) : strtotime($xmlNote['created'])); 114 | 115 | if (isset($xmlNote['tag'])) { 116 | $this->processTag($xmlNote, $noteInstance); 117 | } 118 | 119 | if (isset($xmlNote['resource'])) { 120 | $this->processFile($xmlNote, $noteInstance); 121 | } 122 | } 123 | 124 | /** 125 | * Fetch tags from xml['tag'] 126 | * 127 | * @param $xmlNote 128 | * @param \Note $noteInstance 129 | */ 130 | protected function processTag($xmlNote, $noteInstance) 131 | { 132 | $tagsIds = []; 133 | 134 | // Can be single element if 1 tag 135 | if (!is_array($xmlNote['tag'])) { 136 | $xmlNote['tag'] = [$xmlNote['tag']]; 137 | } 138 | foreach ($xmlNote['tag'] as $tag) { 139 | $tagCreate = $this->createTag($tag); 140 | $tagsIds[] = $tagCreate->id; 141 | } 142 | 143 | $noteInstance->tags()->sync($tagsIds); 144 | } 145 | 146 | /** 147 | * Fetch attachments from xml['resource'] 148 | * Use base64 149 | * Replace attachments links in note's content: createAttachment($fileContent, $fileName, $attachment['mime']); 174 | 175 | $noteVersion = $noteInstance->version()->first(); 176 | 177 | // TODO: review regexp - need to fetch style attribute in another way. 178 | // replace en-media tag by img 179 | if (str_contains($attachment['mime'], 'image')) { 180 | $imageTag = sprintf( 181 | '', 182 | $this->notebook->id, 183 | $noteInstance->id, 184 | $noteVersion->id, 185 | $newAttachment->id 186 | ); 187 | 188 | $noteVersion->content = preg_replace( 189 | '/]*hash="' . $fileHash . '"([^>]*)><\/en-media>/', 190 | $imageTag, 191 | $noteVersion->content 192 | ); 193 | } else { 194 | $linkTag = sprintf( 195 | '%s', 196 | $this->notebook->id, 197 | $noteInstance->id, 198 | $noteVersion->id, 199 | $newAttachment->id, 200 | $fileName 201 | ); 202 | 203 | $noteVersion->content = preg_replace( 204 | '/]*hash="' . $fileHash . '"([^>]*)><\/en-media>/', 205 | $linkTag, 206 | $noteVersion->content 207 | ); 208 | } 209 | 210 | $noteVersion->attachments()->attach($newAttachment); 211 | $noteVersion->save(); 212 | 213 | // Doesn't work. 214 | // \Queue::push('DocumentParserWorker', array('user_id' => \Auth::user()->id, 'document_id' => $newAttachment->id)); 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/routes.php: -------------------------------------------------------------------------------- 1 | "setup/setConfig", "uses" => "SetupController@setConfiguration"]); 38 | Route::get('setup/register', function() { 39 | return View::make('partials/registration-form', array('ajax' => true, 'admin' => false)); 40 | }); 41 | Route::post('setup/register', ["as" => "setup/register", "uses" => "UserController@register"]); 42 | Route::get('setup/finish', ["as" => "setup/finish", "uses" => "SetupController@finishSetup"]); 43 | Route::get('setup/checkDBStatus', ["as" => "setup/checkDBStatus", "uses" => "SetupController@checkDatabaseStatus"]); 44 | Route::get('setup/installDatabase', ["as" => "setup/installDatabase", "uses" => "SetupController@installDatabase"]); 45 | }else{ 46 | if (Config::get('paperwork.sandstorm')) { 47 | Route::get('/login', ["as" => "user/login", "uses" => "UserController@checkSandstormUsers"]); 48 | } else { 49 | Route::get('/login', ["as" => "user/login", "uses" => "UserController@showLoginForm"]); 50 | Route::post('/login', ["as" => "user/login", "uses" => "UserController@login"]); 51 | } 52 | 53 | if (Config::get('paperwork.registration') === "true") { 54 | Route::get("/register", ["as" => "user/register", "uses" => "UserController@showRegistrationForm"]); 55 | Route::post("/register", ["as" => "user/register", "uses" => "UserController@register"]); 56 | } 57 | 58 | if (Config::get('paperwork.forgot_password')) { 59 | Route::any("/request", ["as" => "user/request", "uses" => "UserController@request"]); 60 | Route::any("/reset/{token}", ["as" => "user/reset", "uses" => "UserController@reset"]); 61 | } 62 | 63 | //Authorized Users 64 | Route::group(["before" => "auth"], function () { 65 | App::setLocale(PaperworkHelpers::getUiLanguageFromSession()); 66 | Route::any("/profile", ["as" => "user/profile", "uses" => "UserController@profile"]); 67 | Route::any("/settings", ["as" => "user/settings", "uses" => "UserController@settings"]); 68 | Route::any("/help/{topic?}", ["as" => "user/help", "uses" => "UserController@help"]); 69 | Route::any("/logout", ["as" => "user/logout", "uses" => "UserController@logout"]); 70 | Route::any("/settings/export", ["as" => "user/settings/export", "uses" => "UserController@export"]); 71 | Route::any("/settings/import", ["as" => "user/settings/import", "uses" => "UserController@import"]); 72 | Route::get('/', ["as" => "/", "uses" => "LibraryController@show"]); 73 | 74 | //Administrators 75 | Route::group(['prefix' => 'admin', 'before' => ['admin']], function () { 76 | Route::get('/', ['as' => 'admin/console', 'uses' => 'AdminController@showConsole']); 77 | Route::post('/users/delete', ['as' => 'admin/users/delete', 'uses' => 'AdminController@deleteOrRestoreUsers']); 78 | 79 | if(Config::get('paperwork.registration') === "admin") { 80 | Route::get("/register", ["as" => "user/register", "uses" => "UserController@showRegistrationForm"]); 81 | Route::post("/register", ["as" => "user/register", "uses" => "UserController@register"]); 82 | } 83 | }); 84 | }); 85 | 86 | 87 | Route::get('/templates/{angularTemplate}', function ($angularTemplate) { 88 | return View::make('templates/' . $angularTemplate); 89 | }); 90 | 91 | Route::group(array('prefix' => 'api/v1', 'before' => 'auth'), function () { 92 | App::setLocale(PaperworkHelpers::getUiLanguageFromSession()); 93 | // Route::any('notebook/{num?}', 'ApiNotebooksController@index')->where('num','([0-9]*)'); 94 | Route::resource('notebooks', 'ApiNotebooksController'); 95 | Route::get('/notebooks/{notebookId}/share/{toUserId}/{toUMASK}', 'ApiNotebooksController@share'); 96 | Route::get('/notebooks/{notebookId}/remove-collection', 'ApiNotebooksController@removeNotebookFromCollection'); 97 | Route::resource('tags', 'ApiTagsController'); 98 | Route::resource('notebooks.notes', 'ApiNotesController'); 99 | // I really don't know whether that's a great way to solve this... 100 | Route::get('/notebooks/{notebookId}/notes/{noteId}/move/{toNotebookId}', 'ApiNotesController@move'); 101 | Route::get('/notebooks/{notebookId}/notes/{noteId}/tag/{toTagId}', 'ApiNotesController@tagNote'); 102 | Route::get('/notebooks/{notebookId}/notes/{noteId}/share/{toUserId}/{toUMASK}', 'ApiNotesController@share'); 103 | Route::resource('notebooks.notes.versions', 'ApiVersionsController'); 104 | Route::resource('notebooks.notes.versions.attachments', 'ApiAttachmentsController'); 105 | Route::get('/notebooks/{notebookId}/notes/{noteId}/versions/{versionId}/attachments/{attachmentId}/raw', 'ApiAttachmentsController@raw'); 106 | Route::resource('shortcuts', 'ApiShortcutsController'); 107 | Route::get('/tags/{tagId}/{parentTagId}','ApiTagsController@nest'); 108 | Route::resource('tags', 'ApiTagsController'); 109 | Route::resource('i18n', 'ApiI18nController'); 110 | Route::get('/users/notebooks/{notebookId}', 'ApiUsersController@showNotebook'); 111 | Route::resource('users', 'ApiUsersController'); 112 | Route::resource('settings', 'ApiSettingsController'); 113 | Route::resource('calendar', 'ApiCalendarController'); 114 | Route::post('/notebooks/collections', 'ApiNotebooksController@storeCollection'); 115 | Route::post('/notebooks/collections/{collectionId}/edit', 'ApiNotebooksController@updateCollection'); 116 | 117 | // Special routes 118 | Route::get('/tagged/{num}', 'ApiNotesController@tagged'); 119 | Route::get('/search/{query}', 'ApiNotesController@search'); 120 | }); 121 | 122 | // Route::any('/api/v1/notebooks/(:num?)', array('as' => 'api.v1.notebooks', 'uses' => 'ApiNotebooksController@index')); 123 | // Route::any('/api/v1/notes/(:num?)', array('as' => 'api.v1.notes', 'uses' => 'api.v1.notes@index')); 124 | } 125 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/views/main.blade.php: -------------------------------------------------------------------------------- 1 | @extends("layouts/user-layout") 2 | @section("content") 3 | 4 | @include('modal/messagebox') 5 | @include('modal/notebook') 6 | @include('modal/notebookSelect') 7 | @include('modal/manageTags') 8 | @include('modal/manageNotebooks') 9 | @include('modal/usersSelect') 10 | @include('modal/usersNotebookSelect') 11 | @include('modal/collection') 12 | @include('modal/notebookDelete') 13 | 14 |
15 |
16 |
17 | 20 | 78 | 79 | 123 | 124 | [[-- @if($welcomeNoteSaved == 1) --] 125 | [[-- HTML::script('js/special_note.js') --]] 126 | [[-- @endif --]] 127 |
132 |
139 |

[[ Lang::get('messages.no_notes_in_notebook') ]]

140 |

[[ Lang::get('messages.nothing_here') ]]

141 |

[[ Lang::get('messages.no_notes_in_notebook') ]]

142 |
143 |
144 |
145 |
146 |
147 | 148 | @stop 149 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/views/partials/debug-main.blade.php: -------------------------------------------------------------------------------- 1 |

2 | HTTP headers:
3 | FORWARDED_PROTO:
4 | PERMISSIONS:
5 |
6 | Amount of users: count()?>
7 | Amount of notebooks: count()?>
8 | Amount of tags: count()?>
9 | Amount of notes: count()?>
10 |
11 | Amount of Init Steps: where('batch', '=', 1)->count();?>
12 |

13 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/views/partials/menu-main.blade.php: -------------------------------------------------------------------------------- 1 | 56 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/views/partials/navigation-main.blade.php: -------------------------------------------------------------------------------- 1 | 46 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/views/templates/paperworkNoteShow.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
56 | 57 | 58 | 59 | 60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
73 |
74 |
{{version.timestamp * 1000 | date:'yyyy-MM-dd'}}
75 |
{{version.timestamp * 1000 | date:'HH:mm'}}
76 |
{{version.username}}
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 87 |
88 |
89 | 92 | 101 | 102 | 104 |
105 |
106 |
107 | @include('partials/file-uploader', array('uploadEnabled' => false, 'actionsEnabled' => false)) 108 | 109 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/views/user/debug.blade.php: -------------------------------------------------------------------------------- 1 | @extends("layouts/user-layout") 2 | @section("content") 3 | 4 |
return View::make('user.debug');
5 | 6 |

7 | HTTP headers:
8 | FORWARDED_PROTO:
9 | PERMISSIONS:
10 |
11 | Amount of users: count()?>
12 | Amount of notebooks: count()?>
13 | Amount of tags: count()?>
14 | Amount of notes: count()?>
15 |
16 | Amount of Init Steps: where('batch', '=', 1)->count();?>
17 |

18 | 19 | @stop 20 | -------------------------------------------------------------------------------- /changedfiles/frontend/app/views/user/settings.blade.php: -------------------------------------------------------------------------------- 1 | @extends("layouts/user-layout") 2 | @section("content") 3 | 4 |
5 |

[[ Lang::get('keywords.settings') ]]

6 | @if (Session::get("error")) 7 | 10 | @endif 11 | @if (Session::get("status")) 12 | 15 | @endif 16 | @if ($errors->first('enex_file')!=null) 17 | 20 | @endif 21 | @if ($errors->first('enex_file_success')!=null) 22 | 25 | @endif 26 | 27 | 32 | 33 |
34 |
35 | @include('user/settings/language', array('settings' => $settings, 'languages' => $languages)) 36 |
37 |
38 |
39 |
40 |
41 |

[[ Lang::get('keywords.loading_message') ]]

42 |
43 |
44 |
45 | @include('user/settings/import', array()) 46 |
47 |
48 |
49 | @stop 50 | -------------------------------------------------------------------------------- /changedfiles/storage/.gitignore: -------------------------------------------------------------------------------- 1 | services.manifest -------------------------------------------------------------------------------- /changedfiles/storage/attachments/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JamborJan/paperwork/14d35b48aad2171e54fdb95c9bddcc841d1eae82/changedfiles/storage/attachments/.empty -------------------------------------------------------------------------------- /changedfiles/storage/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /changedfiles/storage/config/.gitignore: -------------------------------------------------------------------------------- 1 | !default_database.json 2 | !default_paperwork.json 3 | !.gitignore -------------------------------------------------------------------------------- /changedfiles/storage/config/database.json: -------------------------------------------------------------------------------- 1 | { 2 | "driver": "mysql", 3 | "database": "paperwork", 4 | "host": "localhost", 5 | "username": "paperwork", 6 | "password": "paperwork", 7 | "port": 3306 8 | } 9 | -------------------------------------------------------------------------------- /changedfiles/storage/config/paperwork.json: -------------------------------------------------------------------------------- 1 | { 2 | "registration": false, 3 | "forgot_password": false, 4 | "showIssueReportingLink": false, 5 | "userAgentLanguage": true 6 | } 7 | -------------------------------------------------------------------------------- /changedfiles/storage/config/setup: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /changedfiles/storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /changedfiles/storage/meta/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /changedfiles/storage/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /changedfiles/storage/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore --------------------------------------------------------------------------------