Build should pass again

This commit is contained in:
Matthew Ross 2020-01-16 15:05:31 -05:00
parent e6233405ff
commit 515765e296
44 changed files with 5270 additions and 5205 deletions

1
.gitignore vendored
View File

@ -34,6 +34,7 @@ npm-debug.log
yarn-error.log yarn-error.log
testem.log testem.log
/typings /typings
.phpunit.result.cache
# e2e # e2e
/e2e/*.js /e2e/*.js

570
package-lock.json generated
View File

@ -5,12 +5,12 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@angular-devkit/architect": { "@angular-devkit/architect": {
"version": "0.803.21", "version": "0.803.22",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.21.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.22.tgz",
"integrity": "sha512-E2K/YexIWVyKM/xmyxvDjkJf+wX9u4c8YYpNaK4htsRzA06juc7N1MhlL/jURZiRl5b/K9sapYeq3tMX76saxA==", "integrity": "sha512-5Gr0LH+Hjd/NLdmi660VBoo3WbzQM7/yeG+ziktb7hbeVaYK4Mejtcg/DJnCoZ3hzlZuZokWVwvpdFo+A9xKbg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@angular-devkit/core": "8.3.21", "@angular-devkit/core": "8.3.22",
"rxjs": "6.4.0" "rxjs": "6.4.0"
}, },
"dependencies": { "dependencies": {
@ -26,23 +26,23 @@
} }
}, },
"@angular-devkit/build-angular": { "@angular-devkit/build-angular": {
"version": "0.803.21", "version": "0.803.22",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.803.21.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.803.22.tgz",
"integrity": "sha512-flfgflvfpwdsm3x/U7QnfbtyZPEbsVipzQAoao1Zo58Beq1a+NsKsWbjrF/x4TSoI2czt0OVWXNytlfXM7LMhg==", "integrity": "sha512-2q9qLsD52D4GACUAuQhvkgQ7vLAhZzdU0jzfs74RTxqUZ3PS6Ltrrwpdg2kp9RlQ53+nSCYjWBDLk1CxoEt4pg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@angular-devkit/architect": "0.803.21", "@angular-devkit/architect": "0.803.22",
"@angular-devkit/build-optimizer": "0.803.21", "@angular-devkit/build-optimizer": "0.803.22",
"@angular-devkit/build-webpack": "0.803.21", "@angular-devkit/build-webpack": "0.803.22",
"@angular-devkit/core": "8.3.21", "@angular-devkit/core": "8.3.22",
"@babel/core": "7.7.5", "@babel/core": "7.7.5",
"@babel/preset-env": "7.7.6", "@babel/preset-env": "7.7.6",
"@ngtools/webpack": "8.3.21", "@ngtools/webpack": "8.3.22",
"ajv": "6.10.2", "ajv": "6.10.2",
"autoprefixer": "9.6.1", "autoprefixer": "9.6.1",
"browserslist": "4.6.6", "browserslist": "4.8.3",
"cacache": "12.0.2", "cacache": "12.0.2",
"caniuse-lite": "1.0.30000989", "caniuse-lite": "1.0.30001019",
"circular-dependency-plugin": "5.2.0", "circular-dependency-plugin": "5.2.0",
"clean-css": "4.2.1", "clean-css": "4.2.1",
"copy-webpack-plugin": "5.1.1", "copy-webpack-plugin": "5.1.1",
@ -119,9 +119,9 @@
} }
}, },
"@angular-devkit/build-optimizer": { "@angular-devkit/build-optimizer": {
"version": "0.803.21", "version": "0.803.22",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.803.21.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.803.22.tgz",
"integrity": "sha512-gNN6kPaF4phZco3TmsrNr9tIEKXYsoSeoaUiDUfgmCYwa7fAqM8Ojh7HX6IQuB2PpVmEwKGlCcSh6xDtB33NjA==", "integrity": "sha512-VIDeQcBn88PjHBTen3BRVA7DJiKEJdDwukx61mUvUDOcY7S5Ot5WqG0nrZifRjha17Z+fl3XuwS9TZNYmlF7WQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"loader-utils": "1.2.3", "loader-utils": "1.2.3",
@ -146,13 +146,13 @@
} }
}, },
"@angular-devkit/build-webpack": { "@angular-devkit/build-webpack": {
"version": "0.803.21", "version": "0.803.22",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.803.21.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.803.22.tgz",
"integrity": "sha512-zCFVla/Xdk8qGVybvnHtoKml2h0/ShasSjT55VNZO1XaTCMqYkQEwwqSGEiVajpauafWjKrKxxBhsmWoI4efAA==", "integrity": "sha512-RDLAhKHfTFzthzeawHEefYB1MxHiU2I32QzXI3XTCpR2XySw5JG9jIVIcsyDHQH1JtIfpHGq8vgfiTsE3r0YWA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@angular-devkit/architect": "0.803.21", "@angular-devkit/architect": "0.803.22",
"@angular-devkit/core": "8.3.21", "@angular-devkit/core": "8.3.22",
"rxjs": "6.4.0" "rxjs": "6.4.0"
}, },
"dependencies": { "dependencies": {
@ -168,9 +168,9 @@
} }
}, },
"@angular-devkit/core": { "@angular-devkit/core": {
"version": "8.3.21", "version": "8.3.22",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.21.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.22.tgz",
"integrity": "sha512-BYyVbrbys535FplX0+GVOlYBg/cyk1U5SRhSxRRFZYi9epVlEBBPk8/6wV4cQPGb6EwXkVj7YtPWXjXcGfzWmA==", "integrity": "sha512-lOEYcvK3MktjR9YZT/cUjiQE5dZxl8rZ/vgWgwDiL7RtzfXTt8lPapoJe7YKS53gLbUYiBNPCtTyTAqnslWgGA==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "6.10.2", "ajv": "6.10.2",
@ -198,28 +198,15 @@
} }
}, },
"@angular-devkit/schematics": { "@angular-devkit/schematics": {
"version": "8.3.21", "version": "8.3.22",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-8.3.21.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-8.3.22.tgz",
"integrity": "sha512-+wH0362CRr/SijVX4w2baY2ANZ4scQ1k2xO8lT+NMeZQkw3IJQPOfwk1IaqiAs2xuBJZcSDH1Gn80+Jh4Dit7w==", "integrity": "sha512-ETLdV1ftT+ZuuiHl6FjFQ4XLQznWMcxWognX+qgByn+DQOXsYRRvZK1L5eG/SG8CKJ8NL5oteTDloDnghARHFw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@angular-devkit/core": "8.3.21", "@angular-devkit/core": "8.3.22",
"rxjs": "6.4.0" "rxjs": "6.4.0"
}, },
"dependencies": { "dependencies": {
"@angular-devkit/core": {
"version": "8.3.21",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.21.tgz",
"integrity": "sha512-BYyVbrbys535FplX0+GVOlYBg/cyk1U5SRhSxRRFZYi9epVlEBBPk8/6wV4cQPGb6EwXkVj7YtPWXjXcGfzWmA==",
"dev": true,
"requires": {
"ajv": "6.10.2",
"fast-json-stable-stringify": "2.0.0",
"magic-string": "0.25.3",
"rxjs": "6.4.0",
"source-map": "0.7.3"
}
},
"rxjs": { "rxjs": {
"version": "6.4.0", "version": "6.4.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
@ -228,12 +215,6 @@
"requires": { "requires": {
"tslib": "^1.9.0" "tslib": "^1.9.0"
} }
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
} }
} }
}, },
@ -246,16 +227,16 @@
} }
}, },
"@angular/cli": { "@angular/cli": {
"version": "8.3.21", "version": "8.3.22",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.3.21.tgz", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.3.22.tgz",
"integrity": "sha512-ZZpA7mMfIobFT06rBNxm8vucAh8W2s0huJZ4iL0BPujnhIr71PL+gDwssySWDEz2q6i4CkH9QRH76DHhtL6VSQ==", "integrity": "sha512-OT2rzwnxwI0ETP7rXCxjxsIAZEYo9wHP/5rRbu3m15GlQ3Bclq34ZDRwC/bRxXL5+1DfmhAs9AjtYNoFoDM4Tg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@angular-devkit/architect": "0.803.21", "@angular-devkit/architect": "0.803.22",
"@angular-devkit/core": "8.3.21", "@angular-devkit/core": "8.3.22",
"@angular-devkit/schematics": "8.3.21", "@angular-devkit/schematics": "8.3.22",
"@schematics/angular": "8.3.21", "@schematics/angular": "8.3.22",
"@schematics/update": "0.803.21", "@schematics/update": "0.803.22",
"@yarnpkg/lockfile": "1.1.0", "@yarnpkg/lockfile": "1.1.0",
"ansi-colors": "4.1.1", "ansi-colors": "4.1.1",
"debug": "^4.1.1", "debug": "^4.1.1",
@ -273,29 +254,6 @@
"uuid": "^3.3.2" "uuid": "^3.3.2"
}, },
"dependencies": { "dependencies": {
"@angular-devkit/architect": {
"version": "0.803.21",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.21.tgz",
"integrity": "sha512-E2K/YexIWVyKM/xmyxvDjkJf+wX9u4c8YYpNaK4htsRzA06juc7N1MhlL/jURZiRl5b/K9sapYeq3tMX76saxA==",
"dev": true,
"requires": {
"@angular-devkit/core": "8.3.21",
"rxjs": "6.4.0"
}
},
"@angular-devkit/core": {
"version": "8.3.21",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.21.tgz",
"integrity": "sha512-BYyVbrbys535FplX0+GVOlYBg/cyk1U5SRhSxRRFZYi9epVlEBBPk8/6wV4cQPGb6EwXkVj7YtPWXjXcGfzWmA==",
"dev": true,
"requires": {
"ajv": "6.10.2",
"fast-json-stable-stringify": "2.0.0",
"magic-string": "0.25.3",
"rxjs": "6.4.0",
"source-map": "0.7.3"
}
},
"ansi-colors": { "ansi-colors": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@ -326,26 +284,11 @@
"glob": "^7.1.3" "glob": "^7.1.3"
} }
}, },
"rxjs": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
"integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true "dev": true
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
} }
} }
}, },
@ -2187,12 +2130,12 @@
} }
}, },
"@ngtools/webpack": { "@ngtools/webpack": {
"version": "8.3.21", "version": "8.3.22",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-8.3.21.tgz", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-8.3.22.tgz",
"integrity": "sha512-DGqmFQ52sV4uB3y3spQTNLa69oU5cwd1yIqMB4GSM+Qp+hozdzrPA2gVH90N2DDhWe8icsSQHAtZQiR9+BDL8g==", "integrity": "sha512-MES7Q0k6GpQEY74cxElUVy7jIaDBSLvY+eOUN2GKL5CznvBSp3+U5px6X7ZjPGzCp7no1L1JkV9g2e0hPatlcw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@angular-devkit/core": "8.3.21", "@angular-devkit/core": "8.3.22",
"enhanced-resolve": "4.1.0", "enhanced-resolve": "4.1.0",
"rxjs": "6.4.0", "rxjs": "6.4.0",
"tree-kill": "1.2.1", "tree-kill": "1.2.1",
@ -2211,53 +2154,23 @@
} }
}, },
"@schematics/angular": { "@schematics/angular": {
"version": "8.3.21", "version": "8.3.22",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.21.tgz", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.22.tgz",
"integrity": "sha512-KahQ+dHvTsGOZwY6IdzqJZLDEn0G89rrK3OY+7okZujoaLM+LXhxlPoznW1udnZJVTa3VNxYGx11fkgLtRJRqA==", "integrity": "sha512-vD+UgPdbEoFPOH6xe2laFpHn/MC9R5C4A/+J9yQ6HBg5kt1YdyIBakvPOcXQCyWr5VZzDmTyMO76rd3zaef3DQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@angular-devkit/core": "8.3.21", "@angular-devkit/core": "8.3.22",
"@angular-devkit/schematics": "8.3.21" "@angular-devkit/schematics": "8.3.22"
},
"dependencies": {
"@angular-devkit/core": {
"version": "8.3.21",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.21.tgz",
"integrity": "sha512-BYyVbrbys535FplX0+GVOlYBg/cyk1U5SRhSxRRFZYi9epVlEBBPk8/6wV4cQPGb6EwXkVj7YtPWXjXcGfzWmA==",
"dev": true,
"requires": {
"ajv": "6.10.2",
"fast-json-stable-stringify": "2.0.0",
"magic-string": "0.25.3",
"rxjs": "6.4.0",
"source-map": "0.7.3"
}
},
"rxjs": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
"integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
}
} }
}, },
"@schematics/update": { "@schematics/update": {
"version": "0.803.21", "version": "0.803.22",
"resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.803.21.tgz", "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.803.22.tgz",
"integrity": "sha512-D3BRvEBF2cJEgogvFaNOfqtTFHHv/ctSRfOeAYWjUxILtb+2DpuZ9h5QYDFhN9MPgz/vRaOqFORa3sEZCRkX4g==", "integrity": "sha512-X+1sJ7YadcYxDqcLX7l7MEAIL3SHIXpCqToQdAZbAE06NdTFvg5eqiKreSdmm7ZdfL0dBe6oXi/yCDVMoL2zcw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@angular-devkit/core": "8.3.21", "@angular-devkit/core": "8.3.22",
"@angular-devkit/schematics": "8.3.21", "@angular-devkit/schematics": "8.3.22",
"@yarnpkg/lockfile": "1.1.0", "@yarnpkg/lockfile": "1.1.0",
"ini": "1.3.5", "ini": "1.3.5",
"pacote": "9.5.5", "pacote": "9.5.5",
@ -2266,19 +2179,6 @@
"semver-intersect": "1.4.0" "semver-intersect": "1.4.0"
}, },
"dependencies": { "dependencies": {
"@angular-devkit/core": {
"version": "8.3.21",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.21.tgz",
"integrity": "sha512-BYyVbrbys535FplX0+GVOlYBg/cyk1U5SRhSxRRFZYi9epVlEBBPk8/6wV4cQPGb6EwXkVj7YtPWXjXcGfzWmA==",
"dev": true,
"requires": {
"ajv": "6.10.2",
"fast-json-stable-stringify": "2.0.0",
"magic-string": "0.25.3",
"rxjs": "6.4.0",
"source-map": "0.7.3"
}
},
"rxjs": { "rxjs": {
"version": "6.4.0", "version": "6.4.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
@ -2293,12 +2193,6 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true "dev": true
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
} }
} }
}, },
@ -2364,9 +2258,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "12.12.22", "version": "13.1.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.22.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.6.tgz",
"integrity": "sha512-r5i93jqbPWGXYXxianGATOxTelkp6ih/U0WVnvaqAvTqM+0U6J3kw6Xk6uq/dWNRkEVw/0SLcO5ORXbVNz4FMQ==", "integrity": "sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==",
"dev": true "dev": true
}, },
"@types/q": { "@types/q": {
@ -2753,14 +2647,6 @@
"dev": true, "dev": true,
"requires": { "requires": {
"type-fest": "^0.8.1" "type-fest": "^0.8.1"
},
"dependencies": {
"type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true
}
} }
}, },
"ansi-html": { "ansi-html": {
@ -3650,14 +3536,14 @@
} }
}, },
"browserslist": { "browserslist": {
"version": "4.6.6", "version": "4.8.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.6.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.3.tgz",
"integrity": "sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==", "integrity": "sha512-iU43cMMknxG1ClEZ2MDKeonKE1CCrFVkQK2AqO2YWFmvIrx4JWrvQ4w4hQez6EpVI8rHTtqh/ruHHDHSOKxvUg==",
"dev": true, "dev": true,
"requires": { "requires": {
"caniuse-lite": "^1.0.30000984", "caniuse-lite": "^1.0.30001017",
"electron-to-chromium": "^1.3.191", "electron-to-chromium": "^1.3.322",
"node-releases": "^1.1.25" "node-releases": "^1.1.44"
} }
}, },
"browserstack": { "browserstack": {
@ -3858,9 +3744,9 @@
} }
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30000989", "version": "1.0.30001019",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001019.tgz",
"integrity": "sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw==", "integrity": "sha512-6ljkLtF1KM5fQ+5ZN0wuyVvvebJxgJPTmScOMaFuQN2QuOzvRJnWSKfzQskQU5IOU4Gap3zasYPIinzwUjoj/g==",
"dev": true "dev": true
}, },
"canonical-path": { "canonical-path": {
@ -3925,9 +3811,9 @@
} }
}, },
"chownr": { "chownr": {
"version": "1.1.2", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
"integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==",
"dev": true "dev": true
}, },
"chrome-trace-event": { "chrome-trace-event": {
@ -4162,18 +4048,18 @@
"dev": true "dev": true
}, },
"compressible": { "compressible": {
"version": "2.0.17", "version": "2.0.18",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
"integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
"dev": true, "dev": true,
"requires": { "requires": {
"mime-db": ">= 1.40.0 < 2" "mime-db": ">= 1.43.0 < 2"
}, },
"dependencies": { "dependencies": {
"mime-db": { "mime-db": {
"version": "1.42.0", "version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
"integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
"dev": true "dev": true
} }
} }
@ -4454,37 +4340,20 @@
} }
}, },
"core-js": { "core-js": {
"version": "3.6.1", "version": "3.6.3",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.1.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.3.tgz",
"integrity": "sha512-186WjSik2iTGfDjfdCZAxv2ormxtKgemjC3SI6PL31qOA0j5LhTDVjHChccoc7brwLvpvLPiMyRlcO88C4l1QQ==" "integrity": "sha512-DOO9b18YHR+Wk5kJ/c5YFbXuUETreD4TrvXb6edzqZE3aAEd0eJIAWghZ9HttMuiON8SVCnU3fqA4rPxRDD1HQ=="
}, },
"core-js-compat": { "core-js-compat": {
"version": "3.6.0", "version": "3.6.3",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.0.tgz", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.3.tgz",
"integrity": "sha512-Z3eCNjGgoYluH89Jt4wVkfYsc/VdLrA2/woX5lm0isO/pCT+P+Y+o65bOuEnjDJLthdwTBxbCVzptTXtc18fJg==", "integrity": "sha512-Y3YNGU3bU1yrnzVodop23ghArbKv4IqkZg9MMOWv/h7KT6NRk1/SzHhWDDlubg2+tlcUzAqgj1/GyeJ9fUKMeg==",
"dev": true, "dev": true,
"requires": { "requires": {
"browserslist": "^4.8.2", "browserslist": "^4.8.3",
"semver": "7.0.0" "semver": "7.0.0"
}, },
"dependencies": { "dependencies": {
"browserslist": {
"version": "4.8.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.2.tgz",
"integrity": "sha512-+M4oeaTplPm/f1pXDw84YohEv7B1i/2Aisei8s4s6k3QsoSHa7i5sz8u/cGQkkatCPxMASKxPualR4wwYgVboA==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001015",
"electron-to-chromium": "^1.3.322",
"node-releases": "^1.1.42"
}
},
"caniuse-lite": {
"version": "1.0.30001016",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001016.tgz",
"integrity": "sha512-yYQ2QfotceRiH4U+h1Us86WJXtVHDmy3nEKIdYPsZCYnOV5/tMgGbmoIlrMzmh2VXlproqYtVaKeGDBkMZifFA==",
"dev": true
},
"semver": { "semver": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
@ -4690,9 +4559,9 @@
"integrity": "sha1-LkYovhncSyFLXAJjDFlx6BFhgGI=" "integrity": "sha1-LkYovhncSyFLXAJjDFlx6BFhgGI="
}, },
"cyclist": { "cyclist": {
"version": "0.2.2", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
"dev": true "dev": true
}, },
"damerau-levenshtein": { "damerau-levenshtein": {
@ -5090,9 +4959,9 @@
"dev": true "dev": true
}, },
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.3.322", "version": "1.3.331",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.331.tgz",
"integrity": "sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA==", "integrity": "sha512-GuDv5gkxwRROYnmIVFUohoyrNapWCKLNn80L7Pa+9WRF/oY4t7XLH7wBMsYBgIRwi8BvEvsGKLKh8kOciOp6kA==",
"dev": true "dev": true
}, },
"elliptic": { "elliptic": {
@ -5355,9 +5224,9 @@
"dev": true "dev": true
}, },
"events": { "events": {
"version": "3.0.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
"integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==",
"dev": true "dev": true
}, },
"eventsource": { "eventsource": {
@ -5924,12 +5793,12 @@
} }
}, },
"fs-minipass": { "fs-minipass": {
"version": "1.2.6", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
"integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==", "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
"dev": true, "dev": true,
"requires": { "requires": {
"minipass": "^2.2.1" "minipass": "^2.6.0"
} }
}, },
"fs-write-stream-atomic": { "fs-write-stream-atomic": {
@ -6540,9 +6409,9 @@
"dev": true "dev": true
}, },
"ignore-walk": { "ignore-walk": {
"version": "3.0.1", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
"dev": true, "dev": true,
"requires": { "requires": {
"minimatch": "^3.0.4" "minimatch": "^3.0.4"
@ -7550,12 +7419,12 @@
} }
}, },
"karma-jasmine": { "karma-jasmine": {
"version": "2.0.1", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-3.1.0.tgz",
"integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", "integrity": "sha512-IVGbC8gap5x5NNCEOsAE77ic8rZtHDt6wmO0fFC5yT5FeB8qKnGTeud2mtKyQ41xl7vZkZ7ZxKr4wMGR6tWN+A==",
"dev": true, "dev": true,
"requires": { "requires": {
"jasmine-core": "^3.3" "jasmine-core": "^3.5.0"
} }
}, },
"karma-jasmine-html-reporter": { "karma-jasmine-html-reporter": {
@ -7827,16 +7696,16 @@
"dev": true "dev": true
}, },
"make-fetch-happen": { "make-fetch-happen": {
"version": "5.0.0", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.0.tgz", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz",
"integrity": "sha512-nFr/vpL1Jc60etMVKeaLOqfGjMMb3tAHFVJWxHOFCFS04Zmd7kGlMxo0l1tzfhoQje0/UPnd0X8OeGUiXXnfPA==", "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==",
"dev": true, "dev": true,
"requires": { "requires": {
"agentkeepalive": "^3.4.1", "agentkeepalive": "^3.4.1",
"cacache": "^12.0.0", "cacache": "^12.0.0",
"http-cache-semantics": "^3.8.1", "http-cache-semantics": "^3.8.1",
"http-proxy-agent": "^2.1.0", "http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.1", "https-proxy-agent": "^2.2.3",
"lru-cache": "^5.1.1", "lru-cache": "^5.1.1",
"mississippi": "^3.0.0", "mississippi": "^3.0.0",
"node-fetch-npm": "^2.0.2", "node-fetch-npm": "^2.0.2",
@ -7845,35 +7714,6 @@
"ssri": "^6.0.0" "ssri": "^6.0.0"
}, },
"dependencies": { "dependencies": {
"cacache": {
"version": "12.0.3",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
"integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==",
"dev": true,
"requires": {
"bluebird": "^3.5.5",
"chownr": "^1.1.1",
"figgy-pudding": "^3.5.1",
"glob": "^7.1.4",
"graceful-fs": "^4.1.15",
"infer-owner": "^1.0.3",
"lru-cache": "^5.1.1",
"mississippi": "^3.0.0",
"mkdirp": "^0.5.1",
"move-concurrently": "^1.0.1",
"promise-inflight": "^1.0.1",
"rimraf": "^2.6.3",
"ssri": "^6.0.1",
"unique-filename": "^1.1.1",
"y18n": "^4.0.0"
}
},
"graceful-fs": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz",
"integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==",
"dev": true
},
"lru-cache": { "lru-cache": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@ -7883,16 +7723,10 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true "dev": true
} }
} }
@ -8194,9 +8028,9 @@
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.9.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"dev": true, "dev": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
@ -8204,20 +8038,20 @@
}, },
"dependencies": { "dependencies": {
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true "dev": true
} }
} }
}, },
"minizlib": { "minizlib": {
"version": "1.2.1", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"minipass": "^2.2.1" "minipass": "^2.9.0"
} }
}, },
"mississippi": { "mississippi": {
@ -8441,9 +8275,9 @@
} }
}, },
"node-releases": { "node-releases": {
"version": "1.1.43", "version": "1.1.45",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.43.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.45.tgz",
"integrity": "sha512-Rmfnj52WNhvr83MvuAWHEqXVoZXCcDQssSOffU4n4XOL9sPrP61mSZ88g25NqmABDvH7PiAlFCzoSCSdzA293w==", "integrity": "sha512-cXvGSfhITKI8qsV116u2FTzH5EWZJfgG7d4cpqwF8I8+1tWpD6AsvvGRKq2onR0DNj1jfqsjkXZsm14JMS7Cyg==",
"dev": true, "dev": true,
"requires": { "requires": {
"semver": "^6.3.0" "semver": "^6.3.0"
@ -8658,10 +8492,13 @@
} }
}, },
"npm-bundled": { "npm-bundled": {
"version": "1.0.6", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
"integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
"dev": true "dev": true,
"requires": {
"npm-normalize-package-bin": "^1.0.1"
}
}, },
"npm-normalize-package-bin": { "npm-normalize-package-bin": {
"version": "1.0.1", "version": "1.0.1",
@ -8682,9 +8519,9 @@
} }
}, },
"npm-packlist": { "npm-packlist": {
"version": "1.4.4", "version": "1.4.7",
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.7.tgz",
"integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==", "integrity": "sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ignore-walk": "^3.0.1", "ignore-walk": "^3.0.1",
@ -8703,9 +8540,9 @@
} }
}, },
"npm-registry-fetch": { "npm-registry-fetch": {
"version": "4.0.0", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.0.tgz", "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.2.tgz",
"integrity": "sha512-Jllq35Jag8dtv0M17ue74XtdQTyqKzuAYGiX9mAjOhkmNjib3bBUgK6mUY61+AHnXeSRobQkpY3/xIOS/omptw==", "integrity": "sha512-Z0IFtPEozNdeZRPh3aHHxdG+ZRpzcbQaJLthsm3VhNf6DScicTFRHZzK82u8RsJUsUHkX+QH/zcB/5pmd20H4A==",
"dev": true, "dev": true,
"requires": { "requires": {
"JSONStream": "^1.3.4", "JSONStream": "^1.3.4",
@ -8713,7 +8550,8 @@
"figgy-pudding": "^3.4.1", "figgy-pudding": "^3.4.1",
"lru-cache": "^5.1.1", "lru-cache": "^5.1.1",
"make-fetch-happen": "^5.0.0", "make-fetch-happen": "^5.0.0",
"npm-package-arg": "^6.1.0" "npm-package-arg": "^6.1.0",
"safe-buffer": "^5.2.0"
}, },
"dependencies": { "dependencies": {
"lru-cache": { "lru-cache": {
@ -8725,10 +8563,16 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
"dev": true
},
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true "dev": true
} }
} }
@ -8976,22 +8820,22 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.0-next.1", "version": "1.17.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0-next.1.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz",
"integrity": "sha512-7MmGr03N7Rnuid6+wyhD9sHNE2n4tFSwExnU2lQl3lIo2ShXWGePY80zYaoMOmILWv57H0amMjZGHNzzGG70Rw==", "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==",
"dev": true, "dev": true,
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.1.4", "is-callable": "^1.1.5",
"is-regex": "^1.0.4", "is-regex": "^1.0.5",
"object-inspect": "^1.7.0", "object-inspect": "^1.7.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.0",
"string.prototype.trimleft": "^2.1.0", "string.prototype.trimleft": "^2.1.1",
"string.prototype.trimright": "^2.1.0" "string.prototype.trimright": "^2.1.1"
} }
}, },
"es-to-primitive": { "es-to-primitive": {
@ -9010,6 +8854,21 @@
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
"dev": true "dev": true
},
"is-callable": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
"integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
"dev": true
},
"is-regex": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
"integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
} }
} }
}, },
@ -9274,18 +9133,6 @@
"mkdirp": "^0.5.0", "mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.3" "yallist": "^3.0.3"
},
"dependencies": {
"minipass": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"dev": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
}
} }
}, },
"yallist": { "yallist": {
@ -9303,12 +9150,12 @@
"dev": true "dev": true
}, },
"parallel-transform": { "parallel-transform": {
"version": "1.1.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
"integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
"dev": true, "dev": true,
"requires": { "requires": {
"cyclist": "~0.2.2", "cyclist": "^1.0.1",
"inherits": "^2.0.3", "inherits": "^2.0.3",
"readable-stream": "^2.1.5" "readable-stream": "^2.1.5"
} }
@ -10261,22 +10108,22 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.0-next.1", "version": "1.17.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0-next.1.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz",
"integrity": "sha512-7MmGr03N7Rnuid6+wyhD9sHNE2n4tFSwExnU2lQl3lIo2ShXWGePY80zYaoMOmILWv57H0amMjZGHNzzGG70Rw==", "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==",
"dev": true, "dev": true,
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.1.4", "is-callable": "^1.1.5",
"is-regex": "^1.0.4", "is-regex": "^1.0.5",
"object-inspect": "^1.7.0", "object-inspect": "^1.7.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.0",
"string.prototype.trimleft": "^2.1.0", "string.prototype.trimleft": "^2.1.1",
"string.prototype.trimright": "^2.1.0" "string.prototype.trimright": "^2.1.1"
} }
}, },
"es-to-primitive": { "es-to-primitive": {
@ -10295,6 +10142,21 @@
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
"dev": true "dev": true
},
"is-callable": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
"integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
"dev": true
},
"is-regex": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
"integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
} }
} }
}, },
@ -10338,9 +10200,9 @@
"dev": true "dev": true
}, },
"regjsparser": { "regjsparser": {
"version": "0.6.1", "version": "0.6.2",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.1.tgz", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.2.tgz",
"integrity": "sha512-7LutE94sz/NKSYegK+/4E77+8DipxF+Qn2Tmu362AcmsF2NYq/wx3+ObvU90TKEhjf7hQoFXo23ajjrXP7eUgg==", "integrity": "sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"jsesc": "~0.5.0" "jsesc": "~0.5.0"
@ -10897,9 +10759,9 @@
"dev": true "dev": true
}, },
"smart-buffer": { "smart-buffer": {
"version": "4.0.2", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",
"integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==",
"dev": true "dev": true
}, },
"snapdragon": { "snapdragon": {
@ -11158,13 +11020,13 @@
} }
}, },
"socks": { "socks": {
"version": "2.3.2", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.3.2.tgz", "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz",
"integrity": "sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ==", "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==",
"dev": true, "dev": true,
"requires": { "requires": {
"ip": "^1.1.5", "ip": "1.1.5",
"smart-buffer": "4.0.2" "smart-buffer": "^4.1.0"
} }
}, },
"socks-proxy-agent": { "socks-proxy-agent": {
@ -11502,9 +11364,9 @@
} }
}, },
"stream-shift": { "stream-shift": {
"version": "1.0.0", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
"dev": true "dev": true
}, },
"streamroller": { "streamroller": {
@ -12001,16 +11863,16 @@
} }
}, },
"ts-node": { "ts-node": {
"version": "8.5.4", "version": "8.6.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.6.1.tgz",
"integrity": "sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw==", "integrity": "sha512-KqPbO7/UuOPE4ANAOV9geZjk6tet6rK2K+DFeEJq6kIXUi0nLkrOMksozGkIlFopOorkStlwar3DdWYrdl7zCw==",
"dev": true, "dev": true,
"requires": { "requires": {
"arg": "^4.1.0", "arg": "^4.1.0",
"diff": "^4.0.1", "diff": "^4.0.1",
"make-error": "^1.1.1", "make-error": "^1.1.1",
"source-map-support": "^0.5.6", "source-map-support": "^0.5.6",
"yn": "^3.0.0" "yn": "^4.0.0"
} }
}, },
"tslib": { "tslib": {
@ -12099,6 +11961,12 @@
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"optional": true "optional": true
}, },
"type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true
},
"type-is": { "type-is": {
"version": "1.6.18", "version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@ -14615,9 +14483,9 @@
"dev": true "dev": true
}, },
"yn": { "yn": {
"version": "3.1.1", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yn/-/yn-4.0.0.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "integrity": "sha512-huWiiCS4TxKc4SfgmTwW1K7JmXPPAmuXWYy4j9qjQo4+27Kni8mGhAAi1cloRWmBe2EqcLgt3IGqQoRL/MtPgg==",
"dev": true "dev": true
}, },
"zone.js": { "zone.js": {

View File

@ -34,7 +34,7 @@
"test": "npm-run-all test:app test:api", "test": "npm-run-all test:app test:api",
"test:app": "ng test --code-coverage --watch=false", "test:app": "ng test --code-coverage --watch=false",
"test:api": "./src/api/vendor/phpunit/phpunit/phpunit -c test/api/phpunit.xml", "test:api": "./src/api/vendor/phpunit/phpunit/phpunit -c test/api/phpunit.xml",
"test:api-single": "./src/api/vendor/phpunit/phpunit/phpunit -c test/api/phpunit.xml -g single", "test:api-single": "./src/api/vendor/phpunit/phpunit/phpunit -c test/api/phpunit.xml --group single",
"test:watch": "ng test --code-coverage --watch", "test:watch": "ng test --code-coverage --watch",
"lint": "ng lint", "lint": "ng lint",
"postinstall": "cd src/api/ && composer update && composer install --optimize-autoloader && cd ../../" "postinstall": "cd src/api/ && composer update && composer install --optimize-autoloader && cd ../../"
@ -52,7 +52,7 @@
"chartist": "^0.11.4", "chartist": "^0.11.4",
"chartist-plugin-tooltips": "^0.0.17", "chartist-plugin-tooltips": "^0.0.17",
"classlist.js": "^1.1.20150312", "classlist.js": "^1.1.20150312",
"core-js": "^3.6.1", "core-js": "^3.6.3",
"dragula": "^3.7.2", "dragula": "^3.7.2",
"highlight.js": "^9.17.1", "highlight.js": "^9.17.1",
"marked": "^0.8.0", "marked": "^0.8.0",
@ -63,8 +63,8 @@
"zone.js": "^0.9.1" "zone.js": "^0.9.1"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^0.803.21", "@angular-devkit/build-angular": "^0.803.22",
"@angular/cli": "^8.3.21", "@angular/cli": "^8.3.22",
"@angular/compiler-cli": "^8.2.14", "@angular/compiler-cli": "^8.2.14",
"@angular/language-service": "^8.2.14", "@angular/language-service": "^8.2.14",
"@types/chartist": "^0.9.46", "@types/chartist": "^0.9.46",
@ -72,7 +72,7 @@
"@types/jasmine": "~3.5.0", "@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.8", "@types/jasminewd2": "~2.0.8",
"@types/marked": "^0.7.2", "@types/marked": "^0.7.2",
"@types/node": "^12.12.22", "@types/node": "^13.1.6",
"bourbon": "6.0.0", "bourbon": "6.0.0",
"bourbon-neat": "4.0.0", "bourbon-neat": "4.0.0",
"codelyzer": "^5.2.1", "codelyzer": "^5.2.1",
@ -82,13 +82,13 @@
"karma": "^4.4.1", "karma": "^4.4.1",
"karma-chrome-launcher": "^3.1.0", "karma-chrome-launcher": "^3.1.0",
"karma-coverage-istanbul-reporter": "^2.1.1", "karma-coverage-istanbul-reporter": "^2.1.1",
"karma-jasmine": "~2.0.1", "karma-jasmine": "~3.1.0",
"karma-jasmine-html-reporter": "^1.5.1", "karma-jasmine-html-reporter": "^1.5.1",
"nodemon": "^2.0.2", "nodemon": "^2.0.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"protractor": "~5.4.2", "protractor": "~5.4.2",
"puppeteer": "^2.0.0", "puppeteer": "^2.0.0",
"ts-node": "^8.5.4", "ts-node": "^8.6.1",
"tslint": "~5.20.1", "tslint": "~5.20.1",
"typescript": "^3.5.3" "typescript": "^3.5.3"
} }

View File

@ -2,60 +2,60 @@
$container = $app->getContainer(); $container = $app->getContainer();
// Inject a Monolog logger into the dependency container // Inject a Monolog logger into the dependency container
$container['logger'] = function($c) { $container['logger'] = function() {
$logger = new Monolog\Logger('API'); $logger = new Monolog\Logger('API');
$fileHandler = new Monolog\Handler\StreamHandler('logs/api.log'); $fileHandler = new Monolog\Handler\StreamHandler('logs/api.log');
$logger->pushHandler($fileHandler); $logger->pushHandler($fileHandler);
return $logger; return $logger;
}; };
// Replace notFoundHandler to use an API response // Replace notFoundHandler to use an API response
$container['notFoundHandler'] = function($c) { $container['notFoundHandler'] = function($c) {
return function($request, $response) use ($c) { return function() use ($c) {
return $c['response'] return $c['response']
->withHeader('Content-Type', 'application/json') ->withHeader('Content-Type', 'application/json')
->write('{ message: "Matching API call not found." }'); ->write('{ message: "Matching API call not found." }');
}; };
}; };
// Replace the errorHandler to use an API response // Replace the errorHandler to use an API response
$container['errorHandler'] = function ($c) { $container['errorHandler'] = function ($c) {
return function ($request, $response, $exception) use ($c) { return function ($exception) use ($c) {
$c['logger']->addError('Server error', $exception->getTrace()); $c['logger']->addError('Server error', $exception->getTrace());
return $c['response']->withStatus(500) return $c['response']->withStatus(500)
->withHeader('Content-Type', 'application/json') ->withHeader('Content-Type', 'application/json')
->write('{ message: "Internal Server Error", error: "' . ->write('{ message: "Internal Server Error", error: "' .
$exception->getMessage() . '" }'); $exception->getMessage() . '" }');
}; };
}; };
$container['phpErrorHandler'] = function ($c) { $container['phpErrorHandler'] = function ($c) {
return function ($request, $response, $exception) use ($c) { return function ($exception) use ($c) {
$c['logger']->addError('Server error', $exception->getTrace()); $c['logger']->addError('Server error', $exception->getTrace());
return $c['response']->withStatus(500) return $c['response']->withStatus(500)
->withHeader('Content-Type', 'application/json') ->withHeader('Content-Type', 'application/json')
->write('{ message: "Internal Server Error", error: "' . ->write('{ message: "Internal Server Error", error: "' .
$exception->getMessage() . '" }'); $exception->getMessage() . '" }');
}; };
}; };
// Routes ending in '/' use route without '/' // Routes ending in '/' use route without '/'
$app->add(function($request, $response, $next) { $app->add(function($request, $response, $next) {
$uri = $request->getUri(); $uri = $request->getUri();
$path = $uri->getPath(); $path = $uri->getPath();
if (strlen($path) > 1 && substr($path, -1) === '/') { if (strlen($path) > 1 && substr($path, -1) === '/') {
$path = substr($path, 0, -1); $path = substr($path, 0, -1);
} }
if ($uri->getPath() !== $path) { if ($uri->getPath() !== $path) {
return $next($request->withUri($uri->withPath($path)), $response); return $next($request->withUri($uri->withPath($path)), $response);
} }
return $next($request, $response); return $next($request, $response);
}); });

View File

@ -1,23 +1,23 @@
{ {
"require": { "require": {
"monolog/monolog": "^1.18", "monolog/monolog": "^2.0",
"gabordemooij/redbean": "^5.1", "gabordemooij/redbean": "^5.4",
"slim/slim": "^3.3", "slim/slim": "^4.3",
"firebase/php-jwt": "^5.0", "firebase/php-jwt": "^5.0",
"phpmailer/phpmailer": "^6.0", "phpmailer/phpmailer": "^6.1",
"phpunit/phpunit": "^6.0", "phpunit/phpunit": "^8.5",
"myclabs/php-enum": "^1.5" "myclabs/php-enum": "^1.7"
}, },
"license": "MIT", "license": "MIT",
"authors": [ "authors": [
{ {
"name": "Matthew Ross", "name": "Matthew Ross",
"email": "root@matthewross.me", "email": "root@matthewross.me",
"homepage": "https://matthewross.me", "homepage": "https://matthewross.me",
"role": "Developer" "role": "Developer"
}
],
"autoload": {
"classmap": [ "controllers", "helpers" ]
} }
],
"autoload": {
"classmap": [ "controllers", "helpers" ]
}
} }

681
src/api/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -3,88 +3,88 @@ use RedBeanPHP\R;
class Activity extends BaseController { class Activity extends BaseController {
public function getActivity($request, $response, $args) { public function getActivity($request, $response, $args) {
$status = $this->secureRoute($request, $response, $status = $this->secureRoute($request, $response,
SecurityLevel::BOARD_ADMIN); SecurityLevel::BOARD_ADMIN);
if ($status !== 200) { if ($status !== 200) {
return $this->jsonResponse($response, $status); return $this->jsonResponse($response, $status);
}
$activity = [];
// TODO: More activity types
if ($args['type'] === 'task') {
if (!$this->checkBoardAccess($this->getBoardId((int)$args['id']),
$request)) {
return $this->jsonResponse($response, 403);
}
$activity = $this->getTaskActivity((int)$args['id']);
}
$this->apiJson->setSuccess();
$this->apiJson->addData($activity);
return $this->jsonResponse($response);
} }
private function getBoardId($taskId) { $activity = [];
$task = R::load('task', $taskId);
$column = R::load('column', $task->column_id);
return $column->board_id; // TODO: More activity types
if ($args['type'] === 'task') {
if (!$this->checkBoardAccess($this->getBoardId((int)$args['id']),
$request)) {
return $this->jsonResponse($response, 403);
}
$activity = $this->getTaskActivity((int)$args['id']);
} }
private function sortLogs($a, $b) { $this->apiJson->setSuccess();
if ($a->timestamp === $b->timestamp) { $this->apiJson->addData($activity);
return 0; // @codeCoverageIgnore
}
return $a->timestamp > $b->timestamp ? -1 : 1; return $this->jsonResponse($response);
}
private function getBoardId($taskId) {
$task = R::load('task', $taskId);
$column = R::load('column', $task->column_id);
return $column->board_id;
}
private function sortLogs($a, $b) {
if ($a->timestamp === $b->timestamp) {
return 0; // @codeCoverageIgnore
} }
private function getTaskActivity($taskId) { return $a->timestamp > $b->timestamp ? -1 : 1;
$task = R::load('task', $taskId); }
$logs = [];
$commentIds = [];
$attachmentIds = [];
foreach ($task->ownComment as $comment) { private function getTaskActivity($taskId) {
$commentIds[] = (int)$comment->id; $task = R::load('task', $taskId);
} $logs = [];
$commentIds = [];
$attachmentIds = [];
foreach ($task->ownAttachment as $attachment) { foreach ($task->ownComment as $comment) {
$attachmentIds[] = (int)$attachment->id; $commentIds[] = (int)$comment->id;
}
$taskActivity = R::find('activity',
'item_type="task" AND item_id=?',
[$taskId]);
$this->addLogItems($logs, $taskActivity);
$commentActivity =
R::find('activity', 'item_type="comment" AND '.
'item_id IN(' . R::genSlots($commentIds) . ')',
$commentIds);
$this->addLogItems($logs, $commentActivity);
$attachmentActivity =
R::find('activity', 'item_type="attachment" AND '.
'item_id IN(' . R::genSlots($attachmentIds) . ')',
$attachmentIds);
$this->addLogItems($logs, $attachmentActivity);
usort($logs, array("Activity", "sortLogs"));
return $logs;
} }
private function addLogItems(&$logs, $items) { foreach ($task->ownAttachment as $attachment) {
foreach ($items as $logItem) { $attachmentIds[] = (int)$attachment->id;
$logs[] = (object)array('text'=>$logItem->log_text,
'timestamp'=>$logItem->timestamp);
}
} }
$taskActivity = R::find('activity',
'item_type="task" AND item_id=?',
[$taskId]);
$this->addLogItems($logs, $taskActivity);
$commentActivity =
R::find('activity', 'item_type="comment" AND '.
'item_id IN(' . R::genSlots($commentIds) . ')',
$commentIds);
$this->addLogItems($logs, $commentActivity);
$attachmentActivity =
R::find('activity', 'item_type="attachment" AND '.
'item_id IN(' . R::genSlots($attachmentIds) . ')',
$attachmentIds);
$this->addLogItems($logs, $attachmentActivity);
usort($logs, array("Activity", "sortLogs"));
return $logs;
}
private function addLogItems(&$logs, $items) {
foreach ($items as $logItem) {
$logs[] = (object)array('text'=>$logItem->log_text,
'timestamp'=>$logItem->timestamp);
}
}
} }

View File

@ -3,131 +3,131 @@ use RedBeanPHP\R;
class Attachments extends BaseController { class Attachments extends BaseController {
public function getAttachment($request, $response, $args) { public function getAttachment($request, $response, $args) {
$status = $this->secureRoute($request, $response, $status = $this->secureRoute($request, $response,
SecurityLevel::USER); SecurityLevel::USER);
if ($status !== 200) { if ($status !== 200) {
return $this->jsonResponse($response, $status); return $this->jsonResponse($response, $status);
} }
$attachment = R::load('attachment', (int)$args['id']); $attachment = R::load('attachment', (int)$args['id']);
if ($attachment->id === 0) { if ($attachment->id === 0) {
$this->logger->addError('Attempt to load attachment ' . $this->logger->addError('Attempt to load attachment ' .
$args['id'] . ' failed.'); $args['id'] . ' failed.');
$this->apiJson->addAlert('error', 'No attachment found for ID ' . $this->apiJson->addAlert('error', 'No attachment found for ID ' .
$args['id'] . '.'); $args['id'] . '.');
return $this->jsonResponse($response); return $this->jsonResponse($response);
} }
if (!$this->checkBoardAccess($this->getBoardId($attachment->task_id), if (!$this->checkBoardAccess($this->getBoardId($attachment->task_id),
$request)) { $request)) {
return $this->jsonResponse($response, 403); return $this->jsonResponse($response, 403);
} }
$this->apiJson->setSuccess(); $this->apiJson->setSuccess();
$this->apiJson->addData($attachment); $this->apiJson->addData($attachment);
return $this->jsonResponse($response);
}
public function addAttachment($request, $response) {
$status = $this->secureRoute($request, $response,
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$attachment = R::dispense('attachment');
if (!BeanLoader::LoadAttachment($attachment, $request->getBody())) {
$attachment->task_id = 0;
}
$task = R::load('task', $attachment->task_id);
if ($task->id === 0) {
$this->logger->addError('Add Attachment: ', [$attachment]);
$this->apiJson->addAlert('error', 'Error adding attachment. ' .
'Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($this->getBoardId($task->id), $request)) {
return $this->jsonResponse($response, 403);
}
R::store($attachment);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added attachment.', '', json_encode($attachment),
'attachment', $attachment->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Attachment added.');
return $this->jsonResponse($response);
}
public function removeAttachment($request, $response, $args) {
$status = $this->secureRoute($request, $response,
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$actor = R::load('user', Auth::GetUserId($request));
$id = (int)$args['id'];
$attachment = R::load('attachment', $id);
// If User level, only the user that created the attachment
// may delete it. If higher level, delete is allowed.
if ((int)$actor->security_level === SecurityLevel::USER) {
if ($actor->id !== $attachment->user_id) {
$this->apiJson->addAlert('error',
'You do not have sufficient permissions ' .
'to remove this attachment.');
return $this->jsonResponse($response); return $this->jsonResponse($response);
}
} // @codeCoverageIgnore
if ((int)$attachment->id !== $id) {
$this->logger->addError('Remove Attachment: ', [$attachment]);
$this->apiJson->addAlert('error', 'Error removing attachment. ' .
'No attachment found for ID ' . $id . '.');
return $this->jsonResponse($response);
} }
public function addAttachment($request, $response) { if (!$this->checkBoardAccess($this->getBoardId($attachment->task_id),
$status = $this->secureRoute($request, $response, $request)) {
SecurityLevel::USER); return $this->jsonResponse($response, 403);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$attachment = R::dispense('attachment');
if (!BeanLoader::LoadAttachment($attachment, $request->getBody())) {
$attachment->task_id = 0;
}
$task = R::load('task', $attachment->task_id);
if ($task->id === 0) {
$this->logger->addError('Add Attachment: ', [$attachment]);
$this->apiJson->addAlert('error', 'Error adding attachment. ' .
'Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($this->getBoardId($task->id), $request)) {
return $this->jsonResponse($response, 403);
}
R::store($attachment);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added attachment.', '', json_encode($attachment),
'attachment', $attachment->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Attachment added.');
return $this->jsonResponse($response);
} }
public function removeAttachment($request, $response, $args) { $before = $attachment;
$status = $this->secureRoute($request, $response, $attachment->delete();
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$actor = R::load('user', Auth::GetUserId($request)); $this->dbLogger->logChange($actor->id,
$actor->username .' removed attachment ' . $before->name,
json_encode($before), '', 'attachment', $id);
$id = (int)$args['id']; $this->apiJson->setSuccess();
$attachment = R::load('attachment', $id); $this->apiJson->addAlert('success',
'Attachment ' . $before->name . ' removed.');
// If User level, only the user that created the attachment return $this->jsonResponse($response);
// may delete it. If higher level, delete is allowed. }
if ((int)$actor->security_level === SecurityLevel::USER) {
if ($actor->id !== $attachment->user_id) {
$this->apiJson->addAlert('error',
'You do not have sufficient permissions ' .
'to remove this attachment.');
return $this->jsonResponse($response); private function getBoardId($taskId) {
} $task = R::load('task', $taskId);
} // @codeCoverageIgnore $column = R::load('column', $task->column_id);
if ((int)$attachment->id !== $id) { return $column->board_id;
$this->logger->addError('Remove Attachment: ', [$attachment]); }
$this->apiJson->addAlert('error', 'Error removing attachment. ' .
'No attachment found for ID ' . $id . '.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($this->getBoardId($attachment->task_id),
$request)) {
return $this->jsonResponse($response, 403);
}
$before = $attachment;
$attachment->delete();
$this->dbLogger->logChange($actor->id,
$actor->username .' removed attachment ' . $before->name,
json_encode($before), '', 'attachment', $id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Attachment ' . $before->name . ' removed.');
return $this->jsonResponse($response);
}
private function getBoardId($taskId) {
$task = R::load('task', $taskId);
$column = R::load('column', $task->column_id);
return $column->board_id;
}
} }

View File

@ -4,292 +4,292 @@ use Firebase\JWT\JWT;
class Auth extends BaseController { class Auth extends BaseController {
public static function HasBoardAccess($request, $boardId, $userId = null) { public static function HasBoardAccess($request, $boardId, $userId = null) {
$hasAccess = false; $hasAccess = false;
if ($userId === null) { if ($userId === null) {
$userId = self::GetUserId($request); $userId = self::GetUserId($request);
}
$user = R::load('user', $userId);
if ((int)$user->security_level === SecurityLevel::ADMIN) {
return true;
}
$board = R::load('board', $boardId);
foreach ($board->sharedUserList as $check) {
if ((int)$check->id === $userId) {
$hasAccess = true;
break;
}
}
return $hasAccess;
} }
public static function CreateInitialAdmin() { $user = R::load('user', $userId);
$admin = R::load('user', 1); if ((int)$user->security_level === SecurityLevel::ADMIN) {
return true;
// Don't create more than one admin
if ($admin->id) {
return;
}
$admin->security_level = SecurityLevel::ADMIN()->getValue();
$admin->username = 'admin';
$admin->password_hash = password_hash('admin', PASSWORD_BCRYPT);
$admin->email = '';
$admin->default_board_id = 0;
$admin->user_option_id = 0;
$admin->last_login = 0;
$admin->active_token = '';
$opts = R::dispense('useroption');
$opts->new_tasks_at_bottom = true;
$opts->show_animations = true;
$opts->show_assignee = true;
$opts->multiple_tasks_per_row = false;
$opts->language = 'en';
R::store($opts);
$admin->user_option_id = $opts->id;
R::store($admin);
} }
public static function CreateJwtSigningKey() { $board = R::load('board', $boardId);
$key = R::load('jwt', 1);
// Don't create more than one secret key foreach ($board->sharedUserList as $check) {
if ($key->id) { if ((int)$check->id === $userId) {
return; $hasAccess = true;
} break;
}
// Generate a JWT signing key by hashing the current time.
$key->secret = hash('sha512', strval(time()));
R::store($key);
} }
public static function ValidateToken($request, $response) { return $hasAccess;
if (!$request->hasHeader('Authorization')) { }
return $response->withStatus(400);
}
$jwt = $request->getHeader('Authorization')[0]; public static function CreateInitialAdmin() {
$payload = self::getJwtPayload($jwt); $admin = R::load('user', 1);
if ($payload === null) { // Don't create more than one admin
return $response->withStatus(401); if ($admin->id) {
} return;
$user = R::load('user', $payload->uid);
if ($user->active_token !== $jwt) {
$user->active_token = '';
R::store($user);
return $response->withStatus(401);
}
$response->getBody()->write($jwt);
return $response;
} }
public static function GetUserId($request) { $admin->security_level = SecurityLevel::ADMIN()->getValue();
$uid = -1; $admin->username = 'admin';
$admin->password_hash = password_hash('admin', PASSWORD_BCRYPT);
$admin->email = '';
$admin->default_board_id = 0;
$admin->user_option_id = 0;
$admin->last_login = 0;
$admin->active_token = '';
try { $opts = R::dispense('useroption');
$jwt = $request->getHeader('Authorization')[0]; $opts->new_tasks_at_bottom = true;
} catch (Exception $ex) { $opts->show_animations = true;
return $uid; $opts->show_assignee = true;
} $opts->multiple_tasks_per_row = false;
$opts->language = 'en';
$payload = self::getJwtPayload($jwt); R::store($opts);
$admin->user_option_id = $opts->id;
R::store($admin);
}
if ($payload !== null) { public static function CreateJwtSigningKey() {
$uid = $payload->uid; $key = R::load('jwt', 1);
}
return $uid; // Don't create more than one secret key
if ($key->id) {
return;
} }
public function login($request, $response) { // Generate a JWT signing key by hashing the current time.
$data = json_decode($request->getBody()); $key->secret = hash('sha512', strval(time()));
$user = R::findOne('user', 'username = ?', [$data->username]);
if ($user === null) { R::store($key);
$this->logger->addError('Login: ', [$data]); }
$this->apiJson->addAlert('error', 'Invalid username or password.');
return $this->jsonResponse($response, 401); public static function ValidateToken($request, $response) {
} if (!$request->hasHeader('Authorization')) {
return $response->withStatus(400);
if (!password_verify($data->password, $user->password_hash)) {
$this->logger->addError('Login: ', [$data]);
$this->apiJson->addAlert('error', 'Invalid username or password.');
return $this->jsonResponse($response, 401);
}
$jwt = self::createJwt($user->id, ($data->remember ? 200 : 1));
$user = R::load('user', $user->id);
if ($user->username === 'admin' && (int)$user->last_login === 0) {
$this->apiJson->addAlert('warn',
'This is your first login, go to Settings ' .
'to change your password.');
$this->apiJson->addAlert('success',
'Go to Settings to create your first board.');
}
$user->active_token = $jwt;
$user->last_login = time();
R::store($user);
$this->dbLogger->logChange($user->id, $user->username . ' logged in',
null, null, 'user', $user->id);
$this->apiJson->setSuccess();
$this->apiJson->addData($jwt);
$this->apiJson->addData($this->sanitizeUser($user));
return $this->jsonResponse($response);
} }
public function logout($request, $response) { $jwt = $request->getHeader('Authorization')[0];
if (!$request->hasHeader('Authorization')) { $payload = self::getJwtPayload($jwt);
return $this->jsonResponse($response, 400);
}
$jwt = $request->getHeader('Authorization')[0]; if ($payload === null) {
$payload = self::getJwtPayload($jwt); return $response->withStatus(401);
if ($payload === null) {
$this->apiJson->addAlert('error', 'Invalid access token.');
return $this->jsonResponse($response, 401);
}
$user = R::load('user', $payload->uid);
if ($user->id) {
$user->active_token = '';
R::store($user);
}
$this->dbLogger->logChange($user->id, $user->username . ' logged out',
null, null, 'user', $user->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'You have been logged out.');
return $this->jsonResponse($response);
} }
public function authenticate($request, $response) { $user = R::load('user', $payload->uid);
$response = self::ValidateToken($request, $response);
$status = $response->getStatusCode();
if ($status !== 200) { if ($user->active_token !== $jwt) {
if ($status === 400) { $user->active_token = '';
$this->apiJson->addAlert('error', R::store($user);
'Authorization header missing.');
return $this->jsonResponse($response, $status);
}
$this->apiJson->addAlert('error', 'Invalid API token.'); return $response->withStatus(401);
return $this->jsonResponse($response, $status);
}
$jwt = $request->getHeader('Authorization')[0];
$payload = self::getJwtPayload($jwt);
$user = R::load('user', $payload->uid);
$opts = R::load('useroption', $user->user_option_id);
$collapsed = R::find('collapsed', ' user_id = ? ', [ $user->id ]);
$user->collapsed = [];
foreach ($collapsed as $collapse) {
$user->collapsed[] = $collapse->column_id;
}
$this->apiJson->setSuccess();
$this->apiJson->addData($jwt);
$this->apiJson->addData($this->sanitizeUser($user));
$this->apiJson->addData($opts);
return $this->jsonResponse($response);
} }
public function refreshToken($request, $response) { $response->getBody()->write($jwt);
$response = self::ValidateToken($request, $response);
$status = $response->getStatusCode();
if ($status !== 200) { return $response;
if ($status === 400) { }
$this->apiJson->addAlert('error',
'Authorization header missing.');
return $this->jsonResponse($response, $status);
}
$this->apiJson->addAlert('error', 'Invalid API token.'); public static function GetUserId($request) {
return $this->jsonResponse($response, $status); $uid = -1;
}
$jwt = $request->getHeader('Authorization')[0]; try {
$payload = self::getJwtPayload($jwt); $jwt = $request->getHeader('Authorization')[0];
} catch (Exception $ex) {
$user = R::load('user', $payload->uid); return $uid;
$jwt = self::createJwt($user->id, (int)$payload->mul);
$user->active_token = $jwt;
R::store($user);
$opts = R::load('useroption', $user->user_option_id);
$this->apiJson->setSuccess();
$this->apiJson->addData($jwt);
$this->apiJson->addData($this->sanitizeUser($user));
$this->apiJson->addData($opts);
return $this->jsonResponse($response);
} }
private function sanitizeUser($user) { $payload = self::getJwtPayload($jwt);
unset($user->password_hash);
unset($user->active_token);
return $user; if ($payload !== null) {
$uid = $payload->uid;
} }
private static function getJwtPayload($jwt) { return $uid;
try { }
$payload = JWT::decode($jwt, self::getJwtKey(), ['HS256']);
} catch (Exception $ex) {
return null;
}
return $payload; public function login($request, $response) {
$data = json_decode($request->getBody());
$user = R::findOne('user', 'username = ?', [$data->username]);
if ($user === null) {
$this->logger->addError('Login: ', [$data]);
$this->apiJson->addAlert('error', 'Invalid username or password.');
return $this->jsonResponse($response, 401);
} }
private static function createJwt($userId, $mult = 1) { if (!password_verify($data->password, $user->password_hash)) {
// If 'remember me' feature is desired, set the multiplier higher. $this->logger->addError('Login: ', [$data]);
// By default, a token will expire after half an hour, but can be $this->apiJson->addAlert('error', 'Invalid username or password.');
// refreshed by a call to /api/refresh.
return JWT::encode(array( return $this->jsonResponse($response, 401);
'exp' => time() + (60 * 30) * $mult, // 30 minutes * $mult
'uid' => (int)$userId,
'mul' => $mult
), Auth::getJwtKey());
} }
private static function getJwtKey() { $jwt = self::createJwt($user->id, ($data->remember ? 200 : 1));
self::CreateJwtSigningKey(); $user = R::load('user', $user->id);
$key = R::load('jwt', 1);
return $key->secret; if ($user->username === 'admin' && (int)$user->last_login === 0) {
$this->apiJson->addAlert('warn',
'This is your first login, go to Settings ' .
'to change your password.');
$this->apiJson->addAlert('success',
'Go to Settings to create your first board.');
} }
$user->active_token = $jwt;
$user->last_login = time();
R::store($user);
$this->dbLogger->logChange($user->id, $user->username . ' logged in',
null, null, 'user', $user->id);
$this->apiJson->setSuccess();
$this->apiJson->addData($jwt);
$this->apiJson->addData($this->sanitizeUser($user));
return $this->jsonResponse($response);
}
public function logout($request, $response) {
if (!$request->hasHeader('Authorization')) {
return $this->jsonResponse($response, 400);
}
$jwt = $request->getHeader('Authorization')[0];
$payload = self::getJwtPayload($jwt);
if ($payload === null) {
$this->apiJson->addAlert('error', 'Invalid access token.');
return $this->jsonResponse($response, 401);
}
$user = R::load('user', $payload->uid);
if ($user->id) {
$user->active_token = '';
R::store($user);
}
$this->dbLogger->logChange($user->id, $user->username . ' logged out',
null, null, 'user', $user->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'You have been logged out.');
return $this->jsonResponse($response);
}
public function authenticate($request, $response) {
$response = self::ValidateToken($request, $response);
$status = $response->getStatusCode();
if ($status !== 200) {
if ($status === 400) {
$this->apiJson->addAlert('error',
'Authorization header missing.');
return $this->jsonResponse($response, $status);
}
$this->apiJson->addAlert('error', 'Invalid API token.');
return $this->jsonResponse($response, $status);
}
$jwt = $request->getHeader('Authorization')[0];
$payload = self::getJwtPayload($jwt);
$user = R::load('user', $payload->uid);
$opts = R::load('useroption', $user->user_option_id);
$collapsed = R::find('collapsed', ' user_id = ? ', [ $user->id ]);
$user->collapsed = [];
foreach ($collapsed as $collapse) {
$user->collapsed[] = $collapse->column_id;
}
$this->apiJson->setSuccess();
$this->apiJson->addData($jwt);
$this->apiJson->addData($this->sanitizeUser($user));
$this->apiJson->addData($opts);
return $this->jsonResponse($response);
}
public function refreshToken($request, $response) {
$response = self::ValidateToken($request, $response);
$status = $response->getStatusCode();
if ($status !== 200) {
if ($status === 400) {
$this->apiJson->addAlert('error',
'Authorization header missing.');
return $this->jsonResponse($response, $status);
}
$this->apiJson->addAlert('error', 'Invalid API token.');
return $this->jsonResponse($response, $status);
}
$jwt = $request->getHeader('Authorization')[0];
$payload = self::getJwtPayload($jwt);
$user = R::load('user', $payload->uid);
$jwt = self::createJwt($user->id, (int)$payload->mul);
$user->active_token = $jwt;
R::store($user);
$opts = R::load('useroption', $user->user_option_id);
$this->apiJson->setSuccess();
$this->apiJson->addData($jwt);
$this->apiJson->addData($this->sanitizeUser($user));
$this->apiJson->addData($opts);
return $this->jsonResponse($response);
}
private function sanitizeUser($user) {
unset($user->password_hash);
unset($user->active_token);
return $user;
}
private static function getJwtPayload($jwt) {
try {
$payload = JWT::decode($jwt, self::getJwtKey(), ['HS256']);
} catch (Exception $ex) {
return null;
}
return $payload;
}
private static function createJwt($userId, $mult = 1) {
// If 'remember me' feature is desired, set the multiplier higher.
// By default, a token will expire after half an hour, but can be
// refreshed by a call to /api/refresh.
return JWT::encode(array(
'exp' => time() + (60 * 30) * $mult, // 30 minutes * $mult
'uid' => (int)$userId,
'mul' => $mult
), Auth::getJwtKey());
}
private static function getJwtKey() {
self::CreateJwtSigningKey();
$key = R::load('jwt', 1);
return $key->secret;
}
} }

View File

@ -3,124 +3,124 @@ use RedBeanPHP\R;
class AutoActions extends BaseController { class AutoActions extends BaseController {
public function getAllActions($request, $response) { public function getAllActions($request, $response) {
$status = $this->secureRoute($request, $response, $status = $this->secureRoute($request, $response,
SecurityLevel::USER); SecurityLevel::USER);
if ($status !== 200) { if ($status !== 200) {
return $this->jsonResponse($response, $status); return $this->jsonResponse($response, $status);
}
$autoActions = $this->getAll($request);
if (!count($autoActions)) {
$this->apiJson->addAlert('info',
'No automatic actions in database.');
$this->apiJson->addData([]);
return $this->jsonResponse($response);
}
$this->apiJson->setSuccess();
$this->apiJson->addData($autoActions);
return $this->jsonResponse($response);
} }
public function addAction($request, $response) { $autoActions = $this->getAll($request);
$status = $this->secureRoute($request, $response,
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$action = R::dispense('autoaction'); if (!count($autoActions)) {
if (!BeanLoader::LoadAutoAction($action, $request->getBody())) { $this->apiJson->addAlert('info',
$action->board_id = 0; 'No automatic actions in database.');
} $this->apiJson->addData([]);
$board = R::load('board', $action->board_id); return $this->jsonResponse($response);
if ($board->id === 0) {
$this->logger->addError('Add Action: ', [$action]);
$this->apiJson->addAlert('error',
'Error adding automatic action. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($action->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
R::store($action);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added automatic action.',
'', json_encode($action), 'action', $action->id);
$actions = $this->getAll($request);
$this->apiJson->setSuccess($actions);
$this->apiJson->addData($actions);
$this->apiJson->addAlert('success', 'Automatic action added.');
return $this->jsonResponse($response);
} }
public function removeAction($request, $response, $args) { $this->apiJson->setSuccess();
$status = $this->secureRoute($request, $response, $this->apiJson->addData($autoActions);
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$id = (int)$args['id']; return $this->jsonResponse($response);
$action = R::load('autoaction', $id); }
if ((int)$action->id !== $id) { public function addAction($request, $response) {
$this->logger->addError('Remove Action: ', [$action]); $status = $this->secureRoute($request, $response,
$this->apiJson->addAlert('error', 'Error removing action. ' . SecurityLevel::BOARD_ADMIN);
'No action found for ID ' . $id . '.'); if ($status !== 200) {
return $this->jsonResponse($response, $status);
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($action->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
$before = $action;
R::trash($action);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username .' removed action ' . $before['id'] . '.',
json_encode($before), '', 'action', $id);
$actions = $this->getAll($request);
$this->apiJson->setSuccess();
$this->apiJson->addData($actions);
$this->apiJson->addAlert('success', 'Automatic action removed.');
return $this->jsonResponse($response);
} }
private function getAll($request) { $action = R::dispense('autoaction');
$autoActions = R::findAll('autoaction'); if (!BeanLoader::LoadAutoAction($action, $request->getBody())) {
$data = []; $action->board_id = 0;
foreach ($autoActions as $action) {
if (Auth::HasBoardAccess($request, $action->board_id)) {
$data[] = $action;
}
}
return $data;
} }
$board = R::load('board', $action->board_id);
if ($board->id === 0) {
$this->logger->addError('Add Action: ', [$action]);
$this->apiJson->addAlert('error',
'Error adding automatic action. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($action->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
R::store($action);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added automatic action.',
'', json_encode($action), 'action', $action->id);
$actions = $this->getAll($request);
$this->apiJson->setSuccess($actions);
$this->apiJson->addData($actions);
$this->apiJson->addAlert('success', 'Automatic action added.');
return $this->jsonResponse($response);
}
public function removeAction($request, $response, $args) {
$status = $this->secureRoute($request, $response,
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$id = (int)$args['id'];
$action = R::load('autoaction', $id);
if ((int)$action->id !== $id) {
$this->logger->addError('Remove Action: ', [$action]);
$this->apiJson->addAlert('error', 'Error removing action. ' .
'No action found for ID ' . $id . '.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($action->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
$before = $action;
R::trash($action);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username .' removed action ' . $before['id'] . '.',
json_encode($before), '', 'action', $id);
$actions = $this->getAll($request);
$this->apiJson->setSuccess();
$this->apiJson->addData($actions);
$this->apiJson->addAlert('success', 'Automatic action removed.');
return $this->jsonResponse($response);
}
private function getAll($request) {
$autoActions = R::findAll('autoaction');
$data = [];
foreach ($autoActions as $action) {
if (Auth::HasBoardAccess($request, $action->board_id)) {
$data[] = $action;
}
}
return $data;
}
} }

View File

@ -2,57 +2,57 @@
use RedBeanPHP\R; use RedBeanPHP\R;
abstract class BaseController { abstract class BaseController {
protected $apiJson; protected $apiJson;
protected $logger; protected $logger;
protected $dbLogger; protected $dbLogger;
protected $container; protected $container;
public function __construct($container) { public function __construct($container) {
$this->apiJson = new ApiJson(); $this->apiJson = new ApiJson();
$this->logger = $container->get('logger'); $this->logger = $container->get('logger');
$this->dbLogger = new DbLogger(); $this->dbLogger = new DbLogger();
$this->container = $container; $this->container = $container;
}
public function jsonResponse($response, $status = 200) {
return $response->withStatus($status)->withJson($this->apiJson);
}
public function checkBoardAccess($boardId, $request) {
if (!Auth::HasBoardAccess($request, $boardId)) {
$this->apiJson->addAlert('error', 'Access restricted.');
return false;
} }
public function jsonResponse($response, $status = 200) { return true;
return $response->withStatus($status)->withJson($this->apiJson); }
}
public function checkBoardAccess($boardId, $request) { public function secureRoute($request, $response, $securityLevel) {
if (!Auth::HasBoardAccess($request, $boardId)) { $response = Auth::ValidateToken($request, $response);
$this->apiJson->addAlert('error', 'Access restricted.'); $status = $response->getStatusCode();
return false;
}
return true;
}
public function secureRoute($request, $response, $securityLevel) {
$response = Auth::ValidateToken($request, $response);
$status = $response->getStatusCode();
if ($status !== 200) {
if ($status === 400) {
$this->apiJson->addAlert('error',
'Authorization header missing.');
return $status;
}
$this->apiJson->addAlert('error', 'Invalid API token.');
return $status;
}
$user = R::load('user', Auth::GetUserId($request));
if ((int)$user->security_level > $securityLevel) {
$this->apiJson->addAlert('error', 'Insufficient privileges.');
return 403;
}
$this->apiJson->addData((string) $response->getBody());
if ($status !== 200) {
if ($status === 400) {
$this->apiJson->addAlert('error',
'Authorization header missing.');
return $status; return $status;
}
$this->apiJson->addAlert('error', 'Invalid API token.');
return $status;
} }
$user = R::load('user', Auth::GetUserId($request));
if ((int)$user->security_level > $securityLevel) {
$this->apiJson->addAlert('error', 'Insufficient privileges.');
return 403;
}
$this->apiJson->addData((string) $response->getBody());
return $status;
}
} }

View File

@ -3,232 +3,224 @@ use RedBeanPHP\R;
class Boards extends BaseController { class Boards extends BaseController {
public function getAllBoards($request, $response) { public function getAllBoards($request, $response) {
$status = $this->secureRoute($request, $response, $status = $this->secureRoute($request, $response,
SecurityLevel::USER); SecurityLevel::USER);
if ($status !== 200) { if ($status !== 200) {
return $this->jsonResponse($response, $status); return $this->jsonResponse($response, $status);
}
$boards = $this->loadAllBoards($request);
if (!count($boards)) {
$this->apiJson->addAlert('info', 'No boards in database.');
return $this->jsonResponse($response);
}
$this->apiJson->setSuccess();
$this->apiJson->addData($boards);
return $this->jsonResponse($response);
} }
public function getBoard($request, $response, $args) { $boards = $this->loadAllBoards($request);
$status = $this->secureRoute($request, $response,
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$board = R::load('board', (int)$args['id']); if (!count($boards)) {
$this->apiJson->addAlert('info', 'No boards in database.');
if ($board->id === 0) { return $this->jsonResponse($response);
$this->logger->addError('Attempt to load board ' . $args['id'] .
' failed.');
$this->apiJson->addAlert('error', 'No board found for ID ' .
$args['id'] . '.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($board->id, $request)) {
return $this->jsonResponse($response, 403);
}
$this->cleanBoard($board);
$this->apiJson->setSuccess();
$this->apiJson->addData(R::exportAll($board));
return $this->jsonResponse($response);
} }
public function addBoard($request, $response) { $this->apiJson->setSuccess();
$status = $this->secureRoute($request, $response, $this->apiJson->addData($boards);
SecurityLevel::ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$board = R::dispense('board'); return $this->jsonResponse($response);
if (!BeanLoader::LoadBoard($board, $request->getBody())) { }
$board->id = -1;
}
$this->includeAdmins($board); public function getBoard($request, $response, $args) {
$status = $this->secureRoute($request, $response,
if ($board->id === -1) { SecurityLevel::USER);
$this->logger->addError('Add Board: ', [$board]); if ($status !== 200) {
$this->apiJson->addAlert('error', 'Error adding board. ' . return $this->jsonResponse($response, $status);
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
R::store($board);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added board ' . $board->name . '.',
'', json_encode($board), 'board', $board->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Board ' . $board->name . ' added.');
$this->apiJson->addData($this->loadAllBoards($request));
return $this->jsonResponse($response);
} }
public function updateBoard($request, $response, $args) { $board = R::load('board', (int)$args['id']);
$status = $this->secureRoute($request, $response,
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$data = json_decode($request->getBody()); if ($board->id === 0) {
$this->logger->addError('Attempt to load board ' . $args['id'] .
' failed.');
$this->apiJson->addAlert('error', 'No board found for ID ' .
$args['id'] . '.');
if (!property_exists($args, 'id')) { return $this->jsonResponse($response);
$this->logger->addError('Update Board: ', [$data]);
$this->apiJson->addAlert('error', 'Error updating board. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
$board = R::load('board', (int)$args['id']);
if (!$this->checkBoardAccess($board->id, $request)) {
return $this->jsonResponse($response, 403);
}
if (!property_exists($data, 'id')) {
$this->logger->addError('Update Board: ', [$board, $data]);
$this->apiJson->addAlert('error', 'Error updating board. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
$update = R::load('board', (int)$args['id']);
$update->id = BeanLoader::LoadBoard($update, $request->getBody())
? $board->id : 0;
if ($update->id === 0 || ($board->id !== $update->id)) {
$this->logger->addError('Update Board: ', [$board, $update]);
$this->apiJson->addAlert('error', 'Error updating board. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
$this->includeAdmins($update);
R::store($update);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' updated board ' . $update->name,
json_encode(R::exportAll($board)),
json_encode(R::exportAll($update)), 'board', $update->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Board ' . $update->name . ' updated.');
$this->apiJson->addData($this->loadAllBoards($request));
return $this->jsonResponse($response);
} }
public function removeBoard($request, $response, $args) { if (!$this->checkBoardAccess($board->id, $request)) {
$status = $this->secureRoute($request, $response, return $this->jsonResponse($response, 403);
SecurityLevel::ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$id = (int)$args['id'];
$board = R::load('board', $id);
if ((int)$board->id !== $id) {
$this->logger->addError('Remove Board: ', [$board]);
$this->apiJson->addAlert('error', 'Error removing board. ' .
'No board found for ID ' . $id . '.');
return $this->jsonResponse($response);
}
$before = $board;
R::trash($board);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' removed board ' . $before->name,
json_encode($before), '', 'board', $id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Board ' . $before->name . ' removed.');
$this->apiJson->addData($this->loadAllBoards($request));
return $this->jsonResponse($response);
} }
private function includeAdmins($board) { $this->cleanBoard($board);
$admins = R::findAll('user', ' WHERE security_level = 1 '); $this->apiJson->setSuccess();
$this->apiJson->addData(R::exportAll($board));
foreach ($admins as $admin) { return $this->jsonResponse($response);
if (!in_array($admin, $board->sharedUserList)) { }
$board->sharedUserList[] = $admin;
} public function addBoard($request, $response) {
} $status = $this->secureRoute($request, $response,
SecurityLevel::ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
} }
private function loadAllBoards($request) { $board = R::dispense('board');
$boards = []; if (!BeanLoader::LoadBoard($board, $request->getBody())) {
$boardBeans = R::findAll('board'); $board->id = -1;
if (count($boardBeans)) {
foreach ($boardBeans as $bean) {
if (Auth::HasBoardAccess($request, $bean->id)) {
$this->cleanBoard($bean);
$boards[] = $bean;
}
}
}
return R::exportAll($boards);
} }
private function cleanBoard(&$board) { $this->includeAdmins($board);
foreach ($board->sharedUserList as $user) {
$user = $this->cleanUser($user);
}
foreach ($board->xownColumnList as $column) { if ($board->id === -1) {
foreach ($column->xownTaskList as $task) { $this->logger->addError('Add Board: ', [$board]);
foreach ($task->sharedUserList as $user) { $this->apiJson->addAlert('error', 'Error adding board. ' .
$user = $this->cleanUser($user); 'Please check your entries and try again.');
}
} return $this->jsonResponse($response);
}
} }
private function cleanUser($user) { R::store($board);
unset($user->password_hash);
unset($user->active_token);
return $user; $actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added board ' . $board->name . '.',
'', json_encode($board), 'board', $board->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Board ' . $board->name . ' added.');
$this->apiJson->addData($this->loadAllBoards($request));
return $this->jsonResponse($response);
}
public function updateBoard($request, $response, $args) {
$status = $this->secureRoute($request, $response,
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
} }
$data = json_decode($request->getBody());
if (is_null($args) || !array_key_exists('id', $args)) {
$this->logger->addError('Update Board: ', [$data]);
$this->apiJson->addAlert('error', 'Error updating board. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
$board = R::load('board', (int)$args['id']);
if (!$this->checkBoardAccess($board->id, $request)) {
return $this->jsonResponse($response, 403);
}
$update = R::load('board', (int)$args['id']);
$update->id = BeanLoader::LoadBoard($update, $request->getBody())
? $board->id : 0;
if ($update->id === 0 || ($board->id !== $update->id)) {
$this->logger->addError('Update Board: ', [$board, $update]);
$this->apiJson->addAlert('error', 'Error updating board. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
$this->includeAdmins($update);
R::store($update);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' updated board ' . $update->name,
json_encode(R::exportAll($board)),
json_encode(R::exportAll($update)), 'board', $update->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Board ' . $update->name . ' updated.');
$this->apiJson->addData($this->loadAllBoards($request));
return $this->jsonResponse($response);
}
public function removeBoard($request, $response, $args) {
$status = $this->secureRoute($request, $response,
SecurityLevel::ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$id = (int)$args['id'];
$board = R::load('board', $id);
if ((int)$board->id !== $id) {
$this->logger->addError('Remove Board: ', [$board]);
$this->apiJson->addAlert('error', 'Error removing board. ' .
'No board found for ID ' . $id . '.');
return $this->jsonResponse($response);
}
$before = $board;
R::trash($board);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' removed board ' . $before->name,
json_encode($before), '', 'board', $id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Board ' . $before->name . ' removed.');
$this->apiJson->addData($this->loadAllBoards($request));
return $this->jsonResponse($response);
}
private function includeAdmins($board) {
$admins = R::findAll('user', ' WHERE security_level = 1 ');
foreach ($admins as $admin) {
if (!in_array($admin, $board->sharedUserList)) {
$board->sharedUserList[] = $admin;
}
}
}
private function loadAllBoards($request) {
$boards = [];
$boardBeans = R::findAll('board');
if (count($boardBeans)) {
foreach ($boardBeans as $bean) {
if (Auth::HasBoardAccess($request, $bean->id)) {
$this->cleanBoard($bean);
$boards[] = $bean;
}
}
}
return R::exportAll($boards);
}
private function cleanBoard(&$board) {
foreach ($board->sharedUserList as $user) {
$user = $this->cleanUser($user);
}
foreach ($board->xownColumnList as $column) {
foreach ($column->xownTaskList as $task) {
foreach ($task->sharedUserList as $user) {
$user = $this->cleanUser($user);
}
}
}
}
private function cleanUser($user) {
unset($user->password_hash);
unset($user->active_token);
return $user;
}
} }

View File

@ -3,151 +3,160 @@ use RedBeanPHP\R;
class Columns extends BaseController { class Columns extends BaseController {
public function getColumn($request, $response, $args) { public function getColumn($request, $response, $args) {
$status = $this->secureRoute($request, $response, $status = $this->secureRoute($request, $response,
SecurityLevel::USER); SecurityLevel::USER);
if ($status !== 200) { if ($status !== 200) {
return $this->jsonResponse($response, $status); return $this->jsonResponse($response, $status);
}
$column = R::load('column', (int)$args['id']);
if ((int)$column->id === 0) {
$this->logger->addError('Attempt to load column ' .
$args['id'] . ' failed.');
$this->apiJson->addAlert('error', 'No column found for ID ' .
$args['id'] . '.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($column->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
$this->apiJson->setSuccess();
$this->apiJson->addData(R::exportAll($column));
return $this->jsonResponse($response);
} }
public function addColumn($request, $response) { $column = R::load('column', (int)$args['id']);
$status = $this->secureRoute($request, $response,
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$column = R::dispense('column'); if ((int)$column->id === 0) {
if (!BeanLoader::LoadColumn($column, $request->getBody())) { $this->logger->addError('Attempt to load column ' .
$column->board_id = 0; $args['id'] . ' failed.');
} $this->apiJson->addAlert('error', 'No column found for ID ' .
$args['id'] . '.');
$board = R::load('board', $column->board_id); return $this->jsonResponse($response);
if ((int)$board->id === 0) {
$this->logger->addError('Add Column: ', [$column]);
$this->apiJson->addAlert('error', 'Error adding column. ' .
'Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($column->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
R::store($column);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added column ' . $column->name . '.',
'', json_encode($column), 'column', $column->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Column ' .
$column->name . ' added.');
return $this->jsonResponse($response);
} }
public function updateColumn($request, $response, $args) { if (!$this->checkBoardAccess($column->board_id, $request)) {
$status = $this->secureRoute($request, $response, return $this->jsonResponse($response, 403);
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$column = R::load('column', (int)$args['id']);
$update = R::dispense('column');
$update->id = BeanLoader::LoadColumn($update, $request->getBody())
? $column->id
: 0;
if ($column->id === 0 || (int)$column->id !== (int)$update->id) {
$this->logger->addError('Update Column: ', [$column, $update]);
$this->apiJson->addAlert('error', 'Error updating column ' .
$update->name . '. Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($column->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
R::store($update);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' updated column ' . $update->name,
json_encode($column), json_encode($update),
'column', $update->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Column ' .
$update->name . ' updated.');
$this->apiJson->addData(R::exportAll($update));
return $this->jsonResponse($response);
} }
public function removeColumn($request, $response, $args) { $this->apiJson->setSuccess();
$status = $this->secureRoute($request, $response, $this->apiJson->addData(R::exportAll($column));
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$id = (int)$args['id']; return $this->jsonResponse($response);
$column = R::load('column', $id); }
if ((int)$column->id !== $id) { public function addColumn($request, $response) {
$this->logger->addError('Remove Column: ', [$column]); $status = $this->secureRoute($request, $response,
$this->apiJson->addAlert('error', 'Error removing column. ' . SecurityLevel::BOARD_ADMIN);
'No column found for ID ' . $id . '.'); if ($status !== 200) {
return $this->jsonResponse($response, $status);
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($column->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
$before = $column;
R::trash($column);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' removed column ' . $before->name,
json_encode($before), '', 'column', $id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Column ' . $before->name . ' removed.');
return $this->jsonResponse($response);
} }
$column = R::dispense('column');
if (!BeanLoader::LoadColumn($column, $request->getBody())) {
$column->board_id = 0;
}
$board = R::load('board', $column->board_id);
if ((int)$board->id === 0) {
$this->logger->addError('Add Column: ', [$column]);
$this->apiJson->addAlert('error', 'Error adding column. ' .
'Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($column->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
R::store($column);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added column ' . $column->name . '.',
'', json_encode($column), 'column', $column->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Column ' .
$column->name . ' added.');
return $this->jsonResponse($response);
}
public function updateColumn($request, $response, $args) {
$status = $this->secureRoute($request, $response,
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$data = json_decode($request->getBody());
if (is_null($args) || !array_key_exists('id', $args)) {
$this->logger->addError('Update Task: ', [$data]);
$this->apiJson->addAlert('error', 'Error updating task. Please try again.');
return $this->jsonResponse($response);
}
$column = R::load('column', (int)$args['id']);
$update = R::dispense('column');
$update->id = BeanLoader::LoadColumn($update, $request->getBody())
? $column->id
: 0;
if ($column->id === 0 || (int)$column->id !== (int)$update->id) {
$this->logger->addError('Update Column: ', [$column, $update]);
$this->apiJson->addAlert('error', 'Error updating column ' .
$update->name . '. Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($column->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
R::store($update);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' updated column ' . $update->name,
json_encode($column), json_encode($update),
'column', $update->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Column ' .
$update->name . ' updated.');
$this->apiJson->addData(R::exportAll($update));
return $this->jsonResponse($response);
}
public function removeColumn($request, $response, $args) {
$status = $this->secureRoute($request, $response,
SecurityLevel::BOARD_ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$id = (int)$args['id'];
$column = R::load('column', $id);
if ((int)$column->id !== $id) {
$this->logger->addError('Remove Column: ', [$column]);
$this->apiJson->addAlert('error', 'Error removing column. ' .
'No column found for ID ' . $id . '.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($column->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
$before = $column;
R::trash($column);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' removed column ' . $before->name,
json_encode($before), '', 'column', $id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Column ' . $before->name . ' removed.');
return $this->jsonResponse($response);
}
} }

View File

@ -3,190 +3,199 @@ use RedBeanPHP\R;
class Comments extends BaseController { class Comments extends BaseController {
public function getComment($request, $response, $args) { public function getComment($request, $response, $args) {
$status = $this->secureRoute($request, $response, $status = $this->secureRoute($request, $response,
SecurityLevel::USER); SecurityLevel::USER);
if ($status !== 200) { if ($status !== 200) {
return $this->jsonResponse($response, $status); return $this->jsonResponse($response, $status);
} }
$comment = R::load('comment', (int)$args['id']); $comment = R::load('comment', (int)$args['id']);
if ($comment->id === 0) { if ($comment->id === 0) {
$this->logger->addError('Attempt to load comment ' . $this->logger->addError('Attempt to load comment ' .
$args['id'] . ' failed.'); $args['id'] . ' failed.');
$this->apiJson->addAlert('error', 'No comment found for ID ' . $this->apiJson->addAlert('error', 'No comment found for ID ' .
$args['id'] . '.'); $args['id'] . '.');
return $this->jsonResponse($response); return $this->jsonResponse($response);
} }
$task = R::load('task', $comment->task_id); $task = R::load('task', $comment->task_id);
$column = R::load('column', $task->column_id); $column = R::load('column', $task->column_id);
if (!$this->checkBoardAccess($column->board_id, $request)) { if (!$this->checkBoardAccess($column->board_id, $request)) {
return $this->jsonResponse($response, 403); return $this->jsonResponse($response, 403);
} }
$this->apiJson->setSuccess(); $this->apiJson->setSuccess();
$this->apiJson->addData($comment); $this->apiJson->addData($comment);
return $this->jsonResponse($response);
}
public function addComment($request, $response) {
$status = $this->secureRoute($request, $response,
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$comment = R::dispense('comment');
if (!BeanLoader::LoadComment($comment, $request->getBody())) {
$comment->task_id = 0;
}
$task = R::load('task', $comment->task_id);
if ($task->id === 0) {
$this->logger->addError('Add Comment: ', [$comment]);
$this->apiJson->addAlert('error', 'Error adding comment. ' .
'Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($this->getBoardId($task->id), $request)) {
return $this->jsonResponse($response, 403);
}
R::store($comment);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added comment ' . $comment->id . '.',
'', json_encode($comment), 'comment', $comment->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Comment added.');
return $this->jsonResponse($response);
}
public function updateComment($request, $response, $args) {
$status = $this->secureRoute($request, $response,
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$data = json_decode($request->getBody());
if (is_null($args) || !$args['id']) {
$this->logger->addError('Update Comment: ', [$data]);
$this->apiJson->addAlert('error', 'Error updating comment. ' .
'Please try again.');
return $this->jsonResponse($response);
}
$actor = R::load('user', Auth::GetUserId($request));
$comment = R::load('comment', (int)$args['id']);
// If User level, only the user that created the comment
// may update it. If higher level, update is allowed.
if ((int)$actor->security_level === SecurityLevel::USER) {
if ($actor->id !== $comment->user_id) {
$this->apiJson->addAlert('error',
'You do not have sufficient permissions ' .
'to update this comment.');
return $this->jsonResponse($response); return $this->jsonResponse($response);
}
} // @codeCoverageIgnore
$update = R::dispense('comment');
$update->id = BeanLoader::LoadComment($update, json_encode($data))
? $comment->id
: 0;
if ($comment->id === 0 || ((int)$comment->id !== (int)$update->id)) {
$this->logger->addError('Update Comment: ', [$comment]);
$this->apiJson->addAlert('error', 'Error updating comment. ' .
'Please try again.');
return $this->jsonResponse($response);
} }
public function addComment($request, $response) { if (!$this->checkBoardAccess(
$status = $this->secureRoute($request, $response, $this->getBoardId($comment->task_id), $request)) {
SecurityLevel::USER); return $this->jsonResponse($response, 403);
if ($status !== 200) { }
return $this->jsonResponse($response, $status);
}
$comment = R::dispense('comment'); R::store($update);
if (!BeanLoader::LoadComment($comment, $request->getBody())) {
$comment->task_id = 0;
}
$task = R::load('task', $comment->task_id); $this->dbLogger->logChange($actor->id,
if ($task->id === 0) { $actor->username . ' updated comment ' . $update->id,
$this->logger->addError('Add Comment: ', [$comment]); json_encode($comment), json_encode($update),
$this->apiJson->addAlert('error', 'Error adding comment. ' . 'comment', $update->id);
'Please try again.');
return $this->jsonResponse($response); $this->apiJson->setSuccess();
} $this->apiJson->addAlert('success', 'Comment updated.');
if (!$this->checkBoardAccess($this->getBoardId($task->id), $request)) { $task = R::load('task', $comment->task_id);
return $this->jsonResponse($response, 403); $this->apiJson->addData(R::exportAll($task));
}
R::store($comment); return $this->jsonResponse($response);
}
$actor = R::load('user', Auth::GetUserId($request)); public function removeComment($request, $response, $args) {
$this->dbLogger->logChange($actor->id, $status = $this->secureRoute($request, $response,
$actor->username . ' added comment ' . $comment->id . '.', SecurityLevel::USER);
'', json_encode($comment), 'comment', $comment->id); if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$this->apiJson->setSuccess(); $actor = R::load('user', Auth::GetUserId($request));
$this->apiJson->addAlert('success', 'Comment added.');
$id = (int)$args['id'];
$comment = R::load('comment', $id);
// If User level, only the user that created the comment
// may delete it. If higher level, delete is allowed.
if ((int)$actor->security_level === SecurityLevel::USER) {
if ($actor->id !== $comment->user_id) {
$this->apiJson->addAlert('error',
'You do not have sufficient permissions ' .
'to remove this comment.');
return $this->jsonResponse($response); return $this->jsonResponse($response);
}
} // @codeCoverageIgnore
if ((int)$comment->id !== $id) {
$this->logger->addError('Remove Comment: ', [$comment]);
$this->apiJson->addAlert('error', 'Error removing comment. ' .
'No comment found for ID ' . $id . '.');
return $this->jsonResponse($response);
} }
public function updateComment($request, $response, $args) { if (!$this->checkBoardAccess(
$status = $this->secureRoute($request, $response, $this->getBoardId($comment->task_id), $request)) {
SecurityLevel::USER); return $this->jsonResponse($response, 403);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$actor = R::load('user', Auth::GetUserId($request));
$comment = R::load('comment', (int)$args['id']);
// If User level, only the user that created the comment
// may update it. If higher level, update is allowed.
if ((int)$actor->security_level === SecurityLevel::USER) {
if ($actor->id !== $comment->user_id) {
$this->apiJson->addAlert('error',
'You do not have sufficient permissions ' .
'to update this comment.');
return $this->jsonResponse($response);
}
} // @codeCoverageIgnore
$data = json_decode($request->getBody());
$update = R::dispense('comment');
$update->id = BeanLoader::LoadComment($update, json_encode($data))
? $comment->id
: 0;
if ($comment->id === 0 || ((int)$comment->id !== (int)$update->id)) {
$this->logger->addError('Update Comment: ', [$comment]);
$this->apiJson->addAlert('error', 'Error updating comment. ' .
'Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess(
$this->getBoardId($comment->task_id), $request)) {
return $this->jsonResponse($response, 403);
}
R::store($update);
$this->dbLogger->logChange($actor->id,
$actor->username . ' updated comment ' . $update->id,
json_encode($comment), json_encode($update),
'comment', $update->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Comment updated.');
$task = R::load('task', $comment->task_id);
$this->apiJson->addData(R::exportAll($task));
return $this->jsonResponse($response);
} }
public function removeComment($request, $response, $args) { $before = $comment;
$status = $this->secureRoute($request, $response, R::trash($comment);
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$actor = R::load('user', Auth::GetUserId($request)); $this->dbLogger->logChange($actor->id,
$actor->username . ' removed comment ' . $before->id,
json_encode($before), '', 'comment', $id);
$id = (int)$args['id']; $this->apiJson->setSuccess();
$comment = R::load('comment', $id); $this->apiJson->addAlert('success', 'Comment removed.');
// If User level, only the user that created the comment $task = R::load('task', $comment->task_id);
// may delete it. If higher level, delete is allowed. $this->apiJson->addData(R::exportAll($task));
if ((int)$actor->security_level === SecurityLevel::USER) {
if ($actor->id !== $comment->user_id) {
$this->apiJson->addAlert('error',
'You do not have sufficient permissions ' .
'to remove this comment.');
return $this->jsonResponse($response); return $this->jsonResponse($response);
} }
} // @codeCoverageIgnore
if ((int)$comment->id !== $id) { private function getBoardId($taskId) {
$this->logger->addError('Remove Comment: ', [$comment]); $task = R::load('task', $taskId);
$this->apiJson->addAlert('error', 'Error removing comment. ' . $column = R::load('column', $task->column_id);
'No comment found for ID ' . $id . '.');
return $this->jsonResponse($response); return $column->board_id;
} }
if (!$this->checkBoardAccess(
$this->getBoardId($comment->task_id), $request)) {
return $this->jsonResponse($response, 403);
}
$before = $comment;
R::trash($comment);
$this->dbLogger->logChange($actor->id,
$actor->username . ' removed comment ' . $before->id,
json_encode($before), '', 'comment', $id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Comment removed.');
$task = R::load('task', $comment->task_id);
$this->apiJson->addData(R::exportAll($task));
return $this->jsonResponse($response);
}
private function getBoardId($taskId) {
$task = R::load('task', $taskId);
$column = R::load('column', $task->column_id);
return $column->board_id;
}
} }

View File

@ -1,22 +1,22 @@
<?php <?php
class Invalid extends BaseController { class Invalid extends BaseController {
public function noApi($request, $response) { public function noApi($request, $response) {
$request; // Not used, but required for Slim Framework $request; // Not used, but required for Slim Framework
$this->apiJson->addAlert('error', $this->apiJson->addAlert('error',
'No API functionality at this endpoint.'); 'No API functionality at this endpoint.');
$apiReturn = new stdClass(); $apiReturn = new stdClass();
$apiReturn->status = 'One of "success" or "failure".'; $apiReturn->status = 'One of "success" or "failure".';
$apiReturn->data = 'An array of data (JSON objects and/or arrays). ' . $apiReturn->data = 'An array of data (JSON objects and/or arrays). ' .
'The first object is a new JWT for the next request.'; 'The first object is a new JWT for the next request.';
$apiReturn->alerts = 'An array of alerts, with "type" of "success", ' . $apiReturn->alerts = 'An array of alerts, with "type" of "success", ' .
'"error", "warn", or "info" and a "text" message.'; '"error", "warn", or "info" and a "text" message.';
$this->apiJson->addData($apiReturn); $this->apiJson->addData($apiReturn);
return $this->jsonResponse($response); return $this->jsonResponse($response);
} }
} }

View File

@ -3,370 +3,384 @@ use RedBeanPHP\R;
class Tasks extends BaseController { class Tasks extends BaseController {
public function getTask($request, $response, $args) { public function getTask($request, $response, $args) {
$status = $this->secureRoute($request, $response, $status = $this->secureRoute($request, $response,
SecurityLevel::USER); SecurityLevel::USER);
if ($status !== 200) { if ($status !== 200) {
return $this->jsonResponse($response, $status); return $this->jsonResponse($response, $status);
}
$task = R::load('task', (int)$args['id']);
if ((int)$task->id === 0) {
$this->logger->addError('Attemt to load task ' .
$args['id'] . ' failed.');
$this->apiJson->addAlert('error', 'No task found for ID ' .
$args['id'] . '.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess(
$this->getBoardId($task->column_id), $request)) {
return $this->jsonResponse($response, 403);
}
$this->apiJson->setSuccess();
$this->apiJson->addData($task);
return $this->jsonResponse($response);
} }
public function addTask($request, $response) { $task = R::load('task', (int)$args['id']);
$status = $this->secureRoute($request, $response,
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$task = R::dispense('task'); if ((int)$task->id === 0) {
BeanLoader::LoadTask($task, $request->getBody()); $this->logger->addError('Attemt to load task ' .
$args['id'] . ' failed.');
$this->apiJson->addAlert('error', 'No task found for ID ' .
$args['id'] . '.');
$column = R::load('column', $task->column_id); return $this->jsonResponse($response);
if ((int)$column->id === 0) {
$this->logger->addError('Add Task: ', [$task]);
$this->apiJson->addAlert('error', 'Error adding task. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess($column->board_id, $request)) {
return $this->jsonResponse($response, 403);
}
R::store($task);
$actor = R::load('user', Auth::GetUserId($request));
$this->updateTaskOrder($task, $actor, true);
$this->checkAutomaticActions(null, $task);
$this->dbLogger->logChange($actor->id,
$actor->username . ' added task ' . $task->title . '.',
'', json_encode($task), 'task', $task->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Task ' .
$task->title . ' added.');
$this->apiJson->addData(R::exportAll($task));
$board = R::load('board', $column->board_id);
$this->apiJson->addData(R::exportAll($board));
return $this->jsonResponse($response);
} }
public function updateTask($request, $response, $args) { if (!$this->checkBoardAccess(
$status = $this->secureRoute($request, $response, $this->getBoardId($task->column_id), $request)) {
SecurityLevel::USER); return $this->jsonResponse($response, 403);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$task = R::load('task', (int)$args['id']);
$update = R::load('task', (int)$args['id']);
$update->id = BeanLoader::LoadTask($update, $request->getBody())
? $task->id
: 0;
if ($task->id === 0 || ((int)$task->id !== (int)$update->id)) {
$this->logger->addError('Update Task: ', [$task, $update]);
$this->apiJson->addAlert('error', 'Error updating task ' .
$task->title . '. Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess(
$this->getBoardId($task->column_id), $request)) {
return $this->jsonResponse($response, 403);
}
$before = R::exportAll($task);
R::store($update);
$after= R::exportAll($update);
$actor = R::load('user', Auth::GetUserId($request));
$this->updateTaskOrder($update, $actor, false);
$this->checkAutomaticActions($before, $after);
$update = R::load('task', $update->id);
$this->dbLogger->logChange($actor->id,
$actor->username . ' updated task ' . $task->title,
json_encode($task), json_encode($update),
'task', $update->id);
$boardId = $this->getBoardId($task->column_id);
$board = R::load('board', $boardId);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Task ' .
$update->title . ' updated.');
$this->apiJson->addData(R::exportAll($update));
$this->apiJson->addData(R::exportAll($board));
return $this->jsonResponse($response);
} }
public function removeTask($request, $response, $args) { $this->apiJson->setSuccess();
$status = $this->secureRoute($request, $response, $this->apiJson->addData($task);
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$id = (int)$args['id']; return $this->jsonResponse($response);
$task = R::load('task', $id); }
if ((int)$task->id !== $id || (int)$task->id === 0) { public function addTask($request, $response) {
$this->logger->addError('Remove Task: ', [$task]); $status = $this->secureRoute($request, $response,
$this->apiJson->addAlert('error', 'Error removing task. ' . SecurityLevel::USER);
'No task found for ID ' . $id . '.'); if ($status !== 200) {
return $this->jsonResponse($response, $status);
return $this->jsonResponse($response);
}
$boardId = $this->getBoardId($task->column_id);
if (!$this->checkBoardAccess($boardId, $request)) {
return $this->jsonResponse($response, 403);
}
$before = $task;
R::trash($task);
$actor = R::load('user', Auth::GetUserId($request));
$this->updateTaskOrder($task, $actor, false);
$this->dbLogger->logChange($actor->id,
$actor->username . ' removed task ' . $before->title,
json_encode($before), '', 'task', $id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Task ' . $before->title . ' removed.');
$board = R::load('board', $boardId);
$this->apiJson->addData(R::exportAll($board));
return $this->jsonResponse($response);
} }
private function getBoardId($columnId) { $task = R::dispense('task');
$column = R::load('column', $columnId); BeanLoader::LoadTask($task, $request->getBody());
return $column->board_id; $column = R::load('column', $task->column_id);
if ((int)$column->id === 0) {
$this->logger->addError('Add Task: ', [$task]);
$this->apiJson->addAlert('error', 'Error adding task. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
} }
private function sortTasks($a, $b) { if (!$this->checkBoardAccess($column->board_id, $request)) {
return strcmp($a->position, $b->position); return $this->jsonResponse($response, 403);
} }
private function updateTaskOrder($task, $user, $isNew) { R::store($task);
$column = R::load('column', $task->column_id);
$user_opts = R::load('useroption', $user->user_option_id);
usort($column->xownTaskList, array($this, 'sortTasks')); $actor = R::load('user', Auth::GetUserId($request));
$this->updateTaskOrder($task, $actor, true);
$this->checkAutomaticActions(null, $task);
$counter = 1; $this->dbLogger->logChange($actor->id,
foreach ($column->xownTaskList as $task) { $actor->username . ' added task ' . $task->title . '.',
$task->position = $counter; '', json_encode($task), 'task', $task->id);
$counter++;
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Task ' .
$task->title . ' added.');
$this->apiJson->addData(R::exportAll($task));
$board = R::load('board', $column->board_id);
$this->apiJson->addData(R::exportAll($board));
return $this->jsonResponse($response);
}
public function updateTask($request, $response, $args) {
$status = $this->secureRoute($request, $response,
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$data = json_decode($request->getBody());
if (is_null($args) || !array_key_exists('id', $args)) {
$this->logger->addError('Update Task: ', [$data]);
$this->apiJson->addAlert('error', 'Error updating task. Please try again.');
return $this->jsonResponse($response);
}
$task = R::load('task', (int)$args['id']);
$update = R::load('task', (int)$args['id']);
$update->id = BeanLoader::LoadTask($update, $request->getBody())
? $task->id
: 0;
if ($task->id === 0 || ((int)$task->id !== (int)$update->id)) {
$this->logger->addError('Update Task: ', [$task, $update]);
$this->apiJson->addAlert('error', 'Error updating task ' .
$task->title . '. Please try again.');
return $this->jsonResponse($response);
}
if (!$this->checkBoardAccess(
$this->getBoardId($task->column_id), $request)) {
return $this->jsonResponse($response, 403);
}
$before = R::exportAll($task);
R::store($update);
$after= R::exportAll($update);
$actor = R::load('user', Auth::GetUserId($request));
$this->updateTaskOrder($update, $actor, false);
$this->checkAutomaticActions($before, $after);
$update = R::load('task', $update->id);
$this->dbLogger->logChange($actor->id,
$actor->username . ' updated task ' . $task->title,
json_encode($task), json_encode($update),
'task', $update->id);
$boardId = $this->getBoardId($task->column_id);
$board = R::load('board', $boardId);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'Task ' .
$update->title . ' updated.');
$this->apiJson->addData(R::exportAll($update));
$this->apiJson->addData(R::exportAll($board));
return $this->jsonResponse($response);
}
public function removeTask($request, $response, $args) {
$status = $this->secureRoute($request, $response,
SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$id = (int)$args['id'];
$task = R::load('task', $id);
if ((int)$task->id !== $id || (int)$task->id === 0) {
$this->logger->addError('Remove Task: ', [$task]);
$this->apiJson->addAlert('error', 'Error removing task. ' .
'No task found for ID ' . $id . '.');
return $this->jsonResponse($response);
}
$boardId = $this->getBoardId($task->column_id);
if (!$this->checkBoardAccess($boardId, $request)) {
return $this->jsonResponse($response, 403);
}
$before = $task;
R::trash($task);
$actor = R::load('user', Auth::GetUserId($request));
$this->updateTaskOrder($task, $actor, false);
$this->dbLogger->logChange($actor->id,
$actor->username . ' removed task ' . $before->title,
json_encode($before), '', 'task', $id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'Task ' . $before->title . ' removed.');
$board = R::load('board', $boardId);
$this->apiJson->addData(R::exportAll($board));
return $this->jsonResponse($response);
}
private function getBoardId($columnId) {
$column = R::load('column', $columnId);
return $column->board_id;
}
private function sortTasks($a, $b) {
return strcmp($a->position, $b->position);
}
private function updateTaskOrder($task, $user, $isNew) {
$column = R::load('column', $task->column_id);
$user_opts = R::load('useroption', $user->user_option_id);
usort($column->xownTaskList, array($this, 'sortTasks'));
$counter = 1;
foreach ($column->xownTaskList as $task) {
$task->position = $counter;
$counter++;
}
R::store($column);
if (!$isNew || $user_opts->new_tasks_at_bottom) {
return;
}
$lastTask = end($column->xownTaskList);
$lastTask->position = 0;
R::store($column);
}
private function checkAutomaticActions($before, $after) {
$boardId = $this->getBoardId(
is_array($after) ? $after[0]['column_id'] : $after['column_id']
);
$autoActions = R::find('autoaction', ' board_id = ? ', [ $boardId ]);
foreach ($autoActions as $action) {
switch ($action->trigger) {
case ActionTrigger::MOVED_TO_COLUMN():
if ($before[0]['column_id'] !== $after[0]['column_id'] &&
$after[0]['column_id'] === (int)$action->source_id) {
$this->alterTask($action, $after[0]['id']);
}
break;
case ActionTrigger::ASSIGNED_TO_USER():
$prevAssigned = $this->isInList($action->source_id,
isset($before[0]['sharedUser']) ?
$before[0]['sharedUser'] :
[]);
if ($prevAssigned) {
break;
} }
R::store($column); foreach ($after[0]['sharedUser'] as $user) {
if ((int)$action->source_id === (int)$user['id']) {
$this->alterTask($action, $after[0]['id']);
}
}
break;
if (!$isNew || $user_opts->new_tasks_at_bottom) { case ActionTrigger::ADDED_TO_CATEGORY():
return; $prevAssigned = $this->isInList($action->source_id,
isset($before[0]['sharedCategory']) ?
$before[0]['sharedCategory'] :
[]);
if ($prevAssigned) {
break;
} }
$lastTask = end($column->xownTaskList); foreach ($after[0]['sharedCategory'] as $category) {
$lastTask->position = 0; if ((int)$action->source_id === (int)$category['id']) {
R::store($column); $this->alterTask($action, $after[0]['id']);
} }
private function checkAutomaticActions($before, $after) {
$boardId = $this->getBoardId($after[0]['column_id']);
$autoActions = R::find('autoaction', ' board_id = ? ', [ $boardId ]);
foreach ($autoActions as $action) {
switch ($action->trigger) {
case ActionTrigger::MOVED_TO_COLUMN():
if ($before[0]['column_id'] !== $after[0]['column_id'] &&
$after[0]['column_id'] === (int)$action->source_id) {
$this->alterTask($action, $after[0]['id']);
}
break;
case ActionTrigger::ASSIGNED_TO_USER():
$prevAssigned = $this->isInList($action->source_id,
isset($before[0]['sharedUser']) ?
$before[0]['sharedUser'] :
[]);
if ($prevAssigned) {
break;
}
foreach ($after[0]['sharedUser'] as $user) {
if ((int)$action->source_id === (int)$user['id']) {
$this->alterTask($action, $after[0]['id']);
}
}
break;
case ActionTrigger::ADDED_TO_CATEGORY():
$prevAssigned = $this->isInList($action->source_id,
isset($before[0]['sharedCategory']) ?
$before[0]['sharedCategory'] :
[]);
if ($prevAssigned) {
break;
}
foreach ($after[0]['sharedCategory'] as $category) {
if ((int)$action->source_id === (int)$category['id']) {
$this->alterTask($action, $after[0]['id']);
}
}
break;
case ActionTrigger::POINTS_CHANGED():
$points = (isset($before[0]['points'])) ?
(int)$before[0]['points'] :
0;
if ($points !== (int)$after[0]['points']) {
$this->updateTaskColor($after[0]['id'],
$points,
$after[0]['points']);
}
break;
}
} }
} break;
private function isInList($itemId, $list) { case ActionTrigger::POINTS_CHANGED():
foreach ($list as $item) { $points = (isset($before[0]['points'])) ?
if ((int)$item['id'] === (int)$itemId) { (int)$before[0]['points'] :
return true; 0;
}
if ($points !== (int)$after[0]['points']) {
$this->updateTaskColor($after[0]['id'],
$points,
$after[0]['points']);
} }
break;
return false; }
}
}
private function isInList($itemId, $list) {
foreach ($list as $item) {
if ((int)$item['id'] === (int)$itemId) {
return true;
}
} }
private function alterTask($action, $taskId) { return false;
switch ($action->type) { }
case ActionType::SET_COLOR():
$task = R::load('task', $taskId);
$task->color = $action->change_to;
$this->apiJson->addAlert('info',
'Task color changed by automatic action.');
R::store($task);
break;
case ActionType::SET_CATEGORY(): private function alterTask($action, $taskId) {
$task = R::load('task', $taskId); switch ($action->type) {
unset($task->sharedCategoryList); case ActionType::SET_COLOR():
case ActionType::ADD_CATEGORY(): $task = R::load('task', $taskId);
if (!isset($task)) { $task->color = $action->change_to;
$task = R::load('task', $taskId); $this->apiJson->addAlert('info',
} 'Task color changed by automatic action.');
R::store($task);
break;
$cat = R::load('category', $action->change_to); case ActionType::SET_CATEGORY():
$task->sharedCategoryList[] = $cat; $task = R::load('task', $taskId);
$this->apiJson->addAlert('info', unset($task->sharedCategoryList);
'Task categories changed by automatic action.'); case ActionType::ADD_CATEGORY():
R::store($task); if (!isset($task)) {
break;
case ActionType::SET_ASSIGNEE():
$task = R::load('task', $taskId);
unset($task->sharedUserList);
case ActionType::ADD_ASSIGNEE():
if (!isset($task)) {
$task = R::load('task', $taskId);
}
$user = R::load('user', $action->change_to);
$task->sharedUserList[] = $user;
$this->apiJson->addAlert('info',
'Task assignees changed by automatic action.');
R::store($task);
break;
case ActionType::CLEAR_DUE_DATE():
$task = R::load('task', $taskId);
if ($task->due_date === '') {
break;
}
$task->due_date = '';
$this->apiJson->addAlert('info',
'Task due date cleared by automatic action.');
R::store($task);
break;
}
}
private function updateTaskColor($taskId, $beforePoints, $afterPoints) {
$task = R::load('task', $taskId); $task = R::load('task', $taskId);
$diff = (float)$beforePoints - (float)$afterPoints; }
// Steps should be between -255 and 255. $cat = R::load('category', $action->change_to);
// Negative = darker, positive = lighter $task->sharedCategoryList[] = $cat;
$steps = max(-255, min(255, $diff * 10)); $this->apiJson->addAlert('info',
'Task categories changed by automatic action.');
R::store($task);
break;
// Normalize into a six character long hex string case ActionType::SET_ASSIGNEE():
$hex = $task->color; $task = R::load('task', $taskId);
$hex = str_replace('#', '', $hex); unset($task->sharedUserList);
if (strlen($hex) == 3) { case ActionType::ADD_ASSIGNEE():
$hex = str_repeat(substr($hex, 0, 1), 2). if (!isset($task)) {
str_repeat(substr($hex, 1, 1), 2). $task = R::load('task', $taskId);
str_repeat(substr($hex, 2, 1), 2); }
}
// Split into three parts: R, G and B $user = R::load('user', $action->change_to);
$colorParts = str_split($hex, 2); $task->sharedUserList[] = $user;
$newColor = '#'; $this->apiJson->addAlert('info',
'Task assignees changed by automatic action.');
R::store($task);
break;
foreach ($colorParts as $color) { case ActionType::CLEAR_DUE_DATE():
// Convert to decimal $task = R::load('task', $taskId);
$color = hexdec($color);
// Adjust color
$color = max(0, min(255, $color + $steps));
// Make two char hex code
$newColor .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT);
}
$task->color = $newColor; if ($task->due_date === '') {
break;
}
$this->apiJson->addAlert('info', $task->due_date = '';
'Task color changed by automatic action.'); $this->apiJson->addAlert('info',
R::store($task); 'Task due date cleared by automatic action.');
R::store($task);
break;
} }
}
private function updateTaskColor($taskId, $beforePoints, $afterPoints) {
$task = R::load('task', $taskId);
$diff = (float)$beforePoints - (float)$afterPoints;
// Steps should be between -255 and 255.
// Negative = darker, positive = lighter
$steps = max(-255, min(255, $diff * 10));
// Normalize into a six character long hex string
$hex = $task->color;
$hex = str_replace('#', '', $hex);
if (strlen($hex) == 3) {
$hex = str_repeat(substr($hex, 0, 1), 2).
str_repeat(substr($hex, 1, 1), 2).
str_repeat(substr($hex, 2, 1), 2);
}
// Split into three parts: R, G and B
$colorParts = str_split($hex, 2);
$newColor = '#';
foreach ($colorParts as $color) {
// Convert to decimal
$color = hexdec($color);
// Adjust color
$color = max(0, min(255, $color + $steps));
// Make two char hex code
$newColor .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT);
}
$task->color = $newColor;
$this->apiJson->addAlert('info',
'Task color changed by automatic action.');
R::store($task);
}
} }

View File

@ -3,482 +3,482 @@ use RedBeanPHP\R;
class Users extends BaseController { class Users extends BaseController {
public function getAllUsers($request, $response) { public function getAllUsers($request, $response) {
$status = $this->secureRoute($request, $response, SecurityLevel::USER); $status = $this->secureRoute($request, $response, SecurityLevel::USER);
if ($status !== 200) { if ($status !== 200) {
return $this->jsonResponse($response, $status); return $this->jsonResponse($response, $status);
} }
$this->apiJson->setSuccess(); $this->apiJson->setSuccess();
$data = $this->getAllUsersCleaned($request); $data = $this->getAllUsersCleaned($request);
$this->apiJson->addData($data); $this->apiJson->addData($data);
return $this->jsonResponse($response);
}
public function getUser($request, $response, $args) {
$status = $this->secureRoute($request, $response, SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$id = (int)$args['id'];
$userIds = $this->getUserIdsByBoardAccess(Auth::GetUserId($request));
$user = R::load('user', $id);
if ($user->id === 0) {
$this->logger->addError('Attempt to load user ' . $id .
' failed.');
$this->apiJson->addAlert('error', 'No user found for ID ' .
$id . '.');
return $this->jsonResponse($response);
}
if (!in_array($id, $userIds)) {
$this->apiJson->addAlert('error', 'Access restricted.');
return $this->jsonResponse($response, 403);
}
$this->apiJson->setSuccess();
$this->apiJson->addData($this->cleanUser($user));
return $this->jsonResponse($response);
}
public function addUser($request, $response) {
$status = $this->secureRoute($request, $response, SecurityLevel::ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$data = json_decode($request->getBody());
$user = R::dispense('user');
if (isset($data->username)) {
if ($this->checkUsernameExists($data)) {
return $this->jsonResponse($response); return $this->jsonResponse($response);
}
} }
public function getUser($request, $response, $args) { if (isset($data->password) &&
$status = $this->secureRoute($request, $response, SecurityLevel::USER); $data->password === $data->password_verify) {
if ($status !== 200) { $data->password_hash =
return $this->jsonResponse($response, $status); password_hash($data->password, PASSWORD_BCRYPT);
} unset($data->password);
unset($data->password_verify);
}
$id = (int)$args['id']; if (!BeanLoader::LoadUser($user, json_encode($data))) {
$user->id = -1;
}
$userIds = $this->getUserIdsByBoardAccess(Auth::GetUserId($request)); if ($user->id === -1) {
$user = R::load('user', $id); $this->logger->addError('Add User: ', [$user]);
$this->apiJson->addAlert('error', 'Error adding user. ' .
'Please check your entries and try again.');
if ($user->id === 0) { return $this->jsonResponse($response);
$this->logger->addError('Attempt to load user ' . $id . }
' failed.');
$this->apiJson->addAlert('error', 'No user found for ID ' .
$id . '.');
return $this->jsonResponse($response); $opts = R::dispense('useroption');
} $opts->new_tasks_at_bottom = true;
$opts->show_animations = true;
$opts->show_assignee = true;
$opts->multiple_tasks_per_row = false;
$opts->language = 'en';
R::store($opts);
if (!in_array($id, $userIds)) { $user->user_option_id = $opts->id;
$this->apiJson->addAlert('error', 'Access restricted.'); R::store($user);
return $this->jsonResponse($response, 403); if (isset($data->default_board_id)) {
} $data->boardAccess[] = $data->default_board_id;
}
$this->apiJson->setSuccess(); $data->id = $user->id;
$this->apiJson->addData($this->cleanUser($user)); $this->updateBoardAccess($data, $request);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added user ' . $user->username . '.',
'', json_encode($user), 'user', $user->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'User ' . $user->username . ' added.');
$this->apiJson->addData($this->getAllUsersCleaned($request));
return $this->jsonResponse($response);
}
public function updateUser($request, $response, $args) {
$status = $this->secureRoute($request, $response, SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$data = json_decode($request->getBody());
$user = R::load('user', (int)$args['id']);
if (!property_exists($data, 'id')) {
$this->logger->addError('Update User: ', [$user, $data]);
$this->apiJson->addAlert('error', 'Error updating user. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
$update = R::load('user', $data->id);
$actor = R::load('user', Auth::GetUserId($request));
if (!$this->checkUserAccess($actor, $user)) {
return $this->jsonResponse($response, 403);
}
$data->password_hash = $user->password_hash;
if (isset($data->new_password) && isset($data->old_password)) {
if (!$this->verifyPassword($data, $user)) {
$this->logger->addError('Update User: ', [$user, $update]);
return $this->jsonResponse($response); return $this->jsonResponse($response);
}
$data->password_hash =
password_hash($data->new_password, PASSWORD_BCRYPT);
unset($data->new_password);
unset($data->old_password);
} }
public function addUser($request, $response) { $data->active_token = $user->active_token;
$status = $this->secureRoute($request, $response, SecurityLevel::ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$data = json_decode($request->getBody()); if (isset($data->password) && $data->password !== '') {
$user = R::dispense('user'); $data->password_hash =
password_hash($data->password, PASSWORD_BCRYPT);
unset($data->password);
}
if (isset($data->username)) { BeanLoader::LoadUser($update, json_encode($data));
if ($this->checkUsernameExists($data)) {
return $this->jsonResponse($response);
}
}
if (isset($data->password) && if ((int)$user->id !== (int)$update->id) {
$data->password === $data->password_verify) { $this->logger->addError('Update User: ', [$user, $update]);
$data->password_hash = $this->apiJson->addAlert('error', 'Error updating user. ' .
password_hash($data->password, PASSWORD_BCRYPT); 'Please check your entries and try again.');
unset($data->password);
unset($data->password_verify);
}
if (!BeanLoader::LoadUser($user, json_encode($data))) { return $this->jsonResponse($response);
$user->id = -1; }
}
if ($user->id === -1) {
$this->logger->addError('Add User: ', [$user]);
$this->apiJson->addAlert('error', 'Error adding user. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
$opts = R::dispense('useroption');
$opts->new_tasks_at_bottom = true;
$opts->show_animations = true;
$opts->show_assignee = true;
$opts->multiple_tasks_per_row = false;
$opts->language = 'en';
R::store($opts);
$user->user_option_id = $opts->id;
R::store($user);
if (isset($data->default_board_id)) {
$data->boardAccess[] = $data->default_board_id;
}
$data->id = $user->id;
$this->updateBoardAccess($data, $request);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' added user ' . $user->username . '.',
'', json_encode($user), 'user', $user->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'User ' . $user->username . ' added.');
$this->apiJson->addData($this->getAllUsersCleaned($request));
if ($user->username !== $update->username) {
if ($this->checkUsernameExists($update)) {
return $this->jsonResponse($response); return $this->jsonResponse($response);
}
} }
public function updateUser($request, $response, $args) { $this->updateDefaultBoardId($data, $user, $update);
$status = $this->secureRoute($request, $response, SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$data = json_decode($request->getBody()); $this->updateBoardAccess($data, $request);
$user = R::load('user', (int)$args['id']); R::store($update);
if (!property_exists($data, 'id')) { $this->dbLogger->logChange($actor->id,
$this->logger->addError('Update User: ', [$user, $data]); $actor->username . ' updated user ' . $update->username,
$this->apiJson->addAlert('error', 'Error updating user. ' . json_encode($user), json_encode($update),
'Please check your entries and try again.'); 'user', $update->id);
return $this->jsonResponse($response); $this->apiJson->setSuccess();
} $this->apiJson->addAlert('success',
'User ' . $update->username . ' updated.');
$this->apiJson->addData(json_encode($this->cleanUser($update)));
$update = R::load('user', $data->id); return $this->jsonResponse($response);
$actor = R::load('user', Auth::GetUserId($request)); }
if (!$this->checkUserAccess($actor, $user)) { public function updateUserOptions($request, $response, $args) {
return $this->jsonResponse($response, 403); $status = $this->secureRoute($request, $response, SecurityLevel::USER);
} if ($status !== 200) {
return $this->jsonResponse($response, $status);
$data->password_hash = $user->password_hash;
if (isset($data->new_password) && isset($data->old_password)) {
if (!$this->verifyPassword($data, $user)) {
$this->logger->addError('Update User: ', [$user, $update]);
return $this->jsonResponse($response);
}
$data->password_hash =
password_hash($data->new_password, PASSWORD_BCRYPT);
unset($data->new_password);
unset($data->old_password);
}
$data->active_token = $user->active_token;
if (isset($data->password) && $data->password !== '') {
$data->password_hash =
password_hash($data->password, PASSWORD_BCRYPT);
unset($data->password);
}
BeanLoader::LoadUser($update, json_encode($data));
if ((int)$user->id !== (int)$update->id) {
$this->logger->addError('Update User: ', [$user, $update]);
$this->apiJson->addAlert('error', 'Error updating user. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
if ($user->username !== $update->username) {
if ($this->checkUsernameExists($update)) {
return $this->jsonResponse($response);
}
}
$this->updateDefaultBoardId($data, $user, $update);
$this->updateBoardAccess($data, $request);
R::store($update);
$this->dbLogger->logChange($actor->id,
$actor->username . ' updated user ' . $update->username,
json_encode($user), json_encode($update),
'user', $update->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'User ' . $update->username . ' updated.');
$this->apiJson->addData(json_encode($this->cleanUser($update)));
return $this->jsonResponse($response);
} }
public function updateUserOptions($request, $response, $args) { $user = R::load('user', (int)$args['id']);
$status = $this->secureRoute($request, $response, SecurityLevel::USER); $actor = R::load('user', Auth::GetUserId($request));
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$user = R::load('user', (int)$args['id']); if ($actor->id !== $user->id) {
$actor = R::load('user', Auth::GetUserId($request)); $this->apiJson->addAlert('error', 'Access restricted.');
if ($actor->id !== $user->id) { return $this->jsonResponse($response, 403);
$this->apiJson->addAlert('error', 'Access restricted.');
return $this->jsonResponse($response, 403);
}
$data = $request->getBody();
$userOpts = R::load('useroption', $user->user_option_id);
$update = R::load('useroption', json_decode($data)->id);
if (!BeanLoader::LoadUserOption($update, $data)) {
$update->id = -1;
}
if ($userOpts->id !== $update->id) {
$this->logger->addError('Update User Options: ',
[$userOpts, $update]);
$this->apiJson->addAlert('error', 'Error updating user options. ' .
'Please check your entries and try again.');
return $this->jsonResponse($response);
}
R::store($update);
$this->dbLogger->logChange($actor->id,
$actor->username . ' updated user options',
json_encode($userOpts), json_encode($update),
'user_option', $update->id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success', 'User options updated.');
$this->apiJson->addData(json_encode($update));
$this->apiJson->addData(json_encode($this->cleanUser($user)));
return $this->jsonResponse($response);
} }
public function toggleCollapsed($request, $response, $args) { $data = $request->getBody();
$status = $this->secureRoute($request, $response, SecurityLevel::USER);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
}
$user = R::load('user', (int)$args['id']); $userOpts = R::load('useroption', $user->user_option_id);
$actor = R::load('user', Auth::GetUserId($request)); $update = R::load('useroption', json_decode($data)->id);
if ($actor->id !== $user->id) { if (!BeanLoader::LoadUserOption($update, $data)) {
$this->apiJson->addAlert('error', 'Access restricted.'); $update->id = -1;
return $this->jsonResponse($response, 403);
}
$data = json_decode($request->getBody());
$collapsed = R::findOne('collapsed', ' user_id = ? AND column_id = ? ',
[ $user->id, $data->id ]);
$makeNew = true;
if (!is_null($collapsed)) {
R::trash($collapsed);
$makeNew = false;
}
if ($makeNew) {
$collapsed = R::dispense('collapsed');
$collapsed->user_id = $user->id;
$collapsed->column_id = $data->id;
R::store($collapsed);
}
$allCollapsed = R::find('collapsed', ' user_id = ? ', [ $user->id ]);
$this->apiJson->setSuccess();
$this->apiJson->addData(R::exportAll($allCollapsed));
return $this->jsonResponse($response);
} }
public function removeUser($request, $response, $args) { if ($userOpts->id !== $update->id) {
$status = $this->secureRoute($request, $response, SecurityLevel::ADMIN); $this->logger->addError('Update User Options: ',
if ($status !== 200) { [$userOpts, $update]);
return $this->jsonResponse($response, $status); $this->apiJson->addAlert('error', 'Error updating user options. ' .
} 'Please check your entries and try again.');
$id = (int)$args['id']; return $this->jsonResponse($response);
$user = R::load('user', $id);
if ((int)$user->id !== $id) {
$this->logger->addError('Remove User: ', [$user]);
$this->apiJson->addAlert('error', 'Error removing user. ' .
'No user found for ID ' . $id . '.');
return $this->jsonResponse($response);
}
$before = $user;
R::trash($user);
$actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' removed user ' . $before->username,
json_encode($before), '', 'user', $id);
$this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'User ' . $before->username . ' removed.');
$this->apiJson->addData($this->getAllUsersCleaned($request));
return $this->jsonResponse($response);
} }
private function updateBoardAccess(&$userData, $request) { R::store($update);
$boardIds = $this->getBoardIdsByAccess($userData->id);
if (isset($userData->boardAccess)) { $this->dbLogger->logChange($actor->id,
$user = R::load('user', $userData->id); $actor->username . ' updated user options',
json_encode($userOpts), json_encode($update),
'user_option', $update->id);
foreach ($userData->boardAccess as $boardId) { $this->apiJson->setSuccess();
if (!in_array($boardId, $boardIds)) { $this->apiJson->addAlert('success', 'User options updated.');
$this->addUserToBoard((int)$boardId, $user, $request); $this->apiJson->addData(json_encode($update));
} $this->apiJson->addData(json_encode($this->cleanUser($user)));
}
if (count(array_diff($userData->boardAccess, $boardIds))) { return $this->jsonResponse($response);
foreach ($boardIds as $removeId) { }
if (!in_array($removeId, $userData->boardAccess)) {
$this->removeUserFromBoard($removeId, $user);
}
}
}
R::store($user); public function toggleCollapsed($request, $response, $args) {
unset($userData->boardAccess); $status = $this->secureRoute($request, $response, SecurityLevel::USER);
} if ($status !== 200) {
return $this->jsonResponse($response, $status);
} }
private function addUserToBoard($boardId, $user, $request) { $user = R::load('user', (int)$args['id']);
if ($boardId > 0 && $actor = R::load('user', Auth::GetUserId($request));
!Auth::HasBoardAccess($request, $boardId, $user->id)) {
$board = R::load('board', $boardId); if ($actor->id !== $user->id) {
$board->sharedUserList[] = $user; $this->apiJson->addAlert('error', 'Access restricted.');
R::store($board);
} return $this->jsonResponse($response, 403);
} }
private function removeUserFromBoard($boardId, $user) { $data = json_decode($request->getBody());
if ($boardId > 0) { $collapsed = R::findOne('collapsed', ' user_id = ? AND column_id = ? ',
$board = R::load('board', $boardId); [ $user->id, $data->id ]);
unset($board->sharedUserList[$user->id]);
R::store($board); $makeNew = true;
} if (!is_null($collapsed)) {
R::trash($collapsed);
$makeNew = false;
} }
private function getAllUsersCleaned($request) { if ($makeNew) {
$userBeans = R::findAll('user'); $collapsed = R::dispense('collapsed');
$userId = Auth::GetUserId($request); $collapsed->user_id = $user->id;
$collapsed->column_id = $data->id;
$userIds = $this->getUserIdsByBoardAccess(Auth::GetUserId($request)); R::store($collapsed);
// If a user has no board access, they should still see themselves
if (count($userIds) === 0) {
$userIds[] = $userId;
}
$actor = R::load('user', $userId);
$isAdmin = ((int)$actor->security_level === SecurityLevel::ADMIN);
$data = [];
foreach ($userBeans as $user) {
if (in_array($user->id, $userIds) || $isAdmin) {
$data[] = $this->cleanUser($user);
}
}
return $data;
} }
private function getBoardIdsByAccess($userId) { $allCollapsed = R::find('collapsed', ' user_id = ? ', [ $user->id ]);
$boardIds = [];
$boards = R::getAll('SELECT board_id FROM board_user ' . $this->apiJson->setSuccess();
'WHERE user_id = :user_id', $this->apiJson->addData(R::exportAll($allCollapsed));
[':user_id' => $userId]);
foreach ($boards as $board) { return $this->jsonResponse($response);
$boardIds[] = (int)$board['board_id']; }
}
return $boardIds; public function removeUser($request, $response, $args) {
$status = $this->secureRoute($request, $response, SecurityLevel::ADMIN);
if ($status !== 200) {
return $this->jsonResponse($response, $status);
} }
private function getUserIdsByBoardAccess($userId) { $id = (int)$args['id'];
$userIds = []; $user = R::load('user', $id);
$boardIds = $this->getBoardIdsByAccess($userId);
foreach ($boardIds as $id) { if ((int)$user->id !== $id) {
$board = R::load('board', $id); $this->logger->addError('Remove User: ', [$user]);
$this->apiJson->addAlert('error', 'Error removing user. ' .
'No user found for ID ' . $id . '.');
foreach ($board->sharedUserList as $user) { return $this->jsonResponse($response);
if (!in_array((int) $user->id, $userIds)) {
$userIds[] = (int) $user->id;
}
}
}
return $userIds;
} }
private function cleanUser($user) { $before = $user;
unset($user->password_hash); R::trash($user);
unset($user->active_token);
$this->setBoardAccess($user); $actor = R::load('user', Auth::GetUserId($request));
$this->dbLogger->logChange($actor->id,
$actor->username . ' removed user ' . $before->username,
json_encode($before), '', 'user', $id);
return $user; $this->apiJson->setSuccess();
$this->apiJson->addAlert('success',
'User ' . $before->username . ' removed.');
$this->apiJson->addData($this->getAllUsersCleaned($request));
return $this->jsonResponse($response);
}
private function updateBoardAccess(&$userData, $request) {
$boardIds = $this->getBoardIdsByAccess($userData->id);
if (isset($userData->boardAccess)) {
$user = R::load('user', $userData->id);
foreach ($userData->boardAccess as $boardId) {
if (!in_array($boardId, $boardIds)) {
$this->addUserToBoard((int)$boardId, $user, $request);
}
}
if (count(array_diff($userData->boardAccess, $boardIds))) {
foreach ($boardIds as $removeId) {
if (!in_array($removeId, $userData->boardAccess)) {
$this->removeUserFromBoard($removeId, $user);
}
}
}
R::store($user);
unset($userData->boardAccess);
}
}
private function addUserToBoard($boardId, $user, $request) {
if ($boardId > 0 &&
!Auth::HasBoardAccess($request, $boardId, $user->id)) {
$board = R::load('board', $boardId);
$board->sharedUserList[] = $user;
R::store($board);
}
}
private function removeUserFromBoard($boardId, $user) {
if ($boardId > 0) {
$board = R::load('board', $boardId);
unset($board->sharedUserList[$user->id]);
R::store($board);
}
}
private function getAllUsersCleaned($request) {
$userBeans = R::findAll('user');
$userId = Auth::GetUserId($request);
$userIds = $this->getUserIdsByBoardAccess(Auth::GetUserId($request));
// If a user has no board access, they should still see themselves
if (count($userIds) === 0) {
$userIds[] = $userId;
} }
private function setBoardAccess(&$user) { $actor = R::load('user', $userId);
$user->board_access = []; $isAdmin = ((int)$actor->security_level === SecurityLevel::ADMIN);
$boards = RedBeanPHP\R::getAll('select bu.board_id, bu.user_id from ' .
'board_user bu join board b on b.id = bu.board_id');
foreach ($boards as $item) { $data = [];
if ((int)$user->id === (int)$item['user_id']) { foreach ($userBeans as $user) {
$user->board_access[] = (int)$item['board_id']; if (in_array($user->id, $userIds) || $isAdmin) {
} $data[] = $this->cleanUser($user);
} }
} }
private function checkUsernameExists($data) { return $data;
$existing = R::findOne('user', 'username = ?', [ $data->username ]); }
if ($existing) { private function getBoardIdsByAccess($userId) {
$this->apiJson->addAlert('error', 'Username already exists. ' . $boardIds = [];
'Change the username and try again.');
return true;
}
return false; $boards = R::getAll('SELECT board_id FROM board_user ' .
'WHERE user_id = :user_id',
[':user_id' => $userId]);
foreach ($boards as $board) {
$boardIds[] = (int)$board['board_id'];
} }
private function checkUserAccess($actor, $user) { return $boardIds;
if ((int)$actor->id !== (int)$user->id) { }
if ((int)$actor->security_level === SecurityLevel::ADMIN) {
return true;
}
$this->apiJson->addAlert('error', 'Access restricted.'); private function getUserIdsByBoardAccess($userId) {
return false; $userIds = [];
$boardIds = $this->getBoardIdsByAccess($userId);
foreach ($boardIds as $id) {
$board = R::load('board', $id);
foreach ($board->sharedUserList as $user) {
if (!in_array((int) $user->id, $userIds)) {
$userIds[] = (int) $user->id;
} }
}
}
return $userIds;
}
private function cleanUser($user) {
unset($user->password_hash);
unset($user->active_token);
$this->setBoardAccess($user);
return $user;
}
private function setBoardAccess(&$user) {
$user->board_access = [];
$boards = RedBeanPHP\R::getAll('select bu.board_id, bu.user_id from ' .
'board_user bu join board b on b.id = bu.board_id');
foreach ($boards as $item) {
if ((int)$user->id === (int)$item['user_id']) {
$user->board_access[] = (int)$item['board_id'];
}
}
}
private function checkUsernameExists($data) {
$existing = R::findOne('user', 'username = ?', [ $data->username ]);
if ($existing) {
$this->apiJson->addAlert('error', 'Username already exists. ' .
'Change the username and try again.');
return true;
}
return false;
}
private function checkUserAccess($actor, $user) {
if ((int)$actor->id !== (int)$user->id) {
if ((int)$actor->security_level === SecurityLevel::ADMIN) {
return true; return true;
}
$this->apiJson->addAlert('error', 'Access restricted.');
return false;
} }
private function verifyPassword($data, $user) { return true;
if (!password_verify($data->old_password, $user->password_hash)) { }
$this->apiJson->addAlert('error', 'Error updating user. ' .
'Incorrect current password.');
return false;
}
return true; private function verifyPassword($data, $user) {
if (!password_verify($data->old_password, $user->password_hash)) {
$this->apiJson->addAlert('error', 'Error updating user. ' .
'Incorrect current password.');
return false;
} }
private function updateDefaultBoardId(&$data, $user, $update) { return true;
if ($user->default_board_id === $update->default_board_id || }
(int)$update->default_board_id === 0) {
return;
}
if (isset($data->boardAccess) && private function updateDefaultBoardId(&$data, $user, $update) {
!in_array($data->default_board_id, $data->boardAccess)) { if ($user->default_board_id === $update->default_board_id ||
$data->boardAccess[] = $data->default_board_id; (int)$update->default_board_id === 0) {
} return;
} }
if (isset($data->boardAccess) &&
!in_array($data->default_board_id, $data->boardAccess)) {
$data->boardAccess[] = $data->default_board_id;
}
}
} }

View File

@ -2,9 +2,9 @@
use MyCLabs\Enum\Enum; use MyCLabs\Enum\Enum;
class ActionTrigger extends Enum { class ActionTrigger extends Enum {
const MOVED_TO_COLUMN = 1; const MOVED_TO_COLUMN = 1;
const ASSIGNED_TO_USER = 2; const ASSIGNED_TO_USER = 2;
const ADDED_TO_CATEGORY = 3; const ADDED_TO_CATEGORY = 3;
const POINTS_CHANGED = 4; const POINTS_CHANGED = 4;
} }

View File

@ -2,12 +2,12 @@
use MyCLabs\Enum\Enum; use MyCLabs\Enum\Enum;
class ActionType extends Enum { class ActionType extends Enum {
const SET_COLOR = 1; const SET_COLOR = 1;
const SET_CATEGORY = 2; const SET_CATEGORY = 2;
const ADD_CATEGORY = 3; const ADD_CATEGORY = 3;
const SET_ASSIGNEE = 4; const SET_ASSIGNEE = 4;
const ADD_ASSIGNEE = 5; const ADD_ASSIGNEE = 5;
const CLEAR_DUE_DATE = 6; const CLEAR_DUE_DATE = 6;
const ALTER_COLOR_BY_POINTS= 7; const ALTER_COLOR_BY_POINTS= 7;
} }

View File

@ -1,22 +1,22 @@
<?php <?php
class ApiJson { class ApiJson {
public $status = 'failure'; public $status = 'failure';
public $data = []; public $data = [];
public $alerts = []; public $alerts = [];
function setSuccess() { function setSuccess() {
$this->status = 'success'; $this->status = 'success';
} }
function addData($obj) { function addData($obj) {
$this->data[] = $obj; $this->data[] = $obj;
} }
function addAlert($type, $text) { function addAlert($type, $text) {
$this->alerts[] = [ $this->alerts[] = [
'type' => $type, 'type' => $type,
'text' => $text 'text' => $text
]; ];
} }
} }

View File

@ -3,300 +3,300 @@ use RedBeanPHP\R;
class BeanLoader { class BeanLoader {
public static function LoadAttachment(&$attachment, $json) { public static function LoadAttachment(&$attachment, $json) {
$data = json_decode($json); $data = json_decode($json);
$attachment->filename = isset($data->filename) ? $data->filename : ''; $attachment->filename = isset($data->filename) ? $data->filename : '';
$attachment->name = isset($data->name) ? $data->name : ''; $attachment->name = isset($data->name) ? $data->name : '';
$attachment->type = isset($data->type) ? $data->type : ''; $attachment->type = isset($data->type) ? $data->type : '';
$attachment->user_id = isset($data->user_id) ? $data->user_id : -1; $attachment->user_id = isset($data->user_id) ? $data->user_id : -1;
$attachment->timestamp = time(); $attachment->timestamp = time();
$attachment->task_id = isset($data->task_id) ? $data->task_id : -1; $attachment->task_id = isset($data->task_id) ? $data->task_id : -1;
if (!isset($data->filename) || !isset($data->name) || if (!isset($data->filename) || !isset($data->name) ||
!isset($data->type) || !isset($data->user_id) || !isset($data->type) || !isset($data->user_id) ||
!isset($data->task_id)) { !isset($data->task_id)) {
return false; return false;
}
return true;
} }
public static function LoadAutoAction(&$action, $json) { return true;
$data = json_decode($json); }
$action->trigger = isset($data->trigger) ? $data->trigger : -1; public static function LoadAutoAction(&$action, $json) {
$action->source_id = isset($data->source_id) ? $data->source_id: -1; $data = json_decode($json);
$action->type = isset($data->type) ? $data->type : '';
$action->change_to = isset($data->change_to) ? $data->change_to: -1;
$action->board_id = isset($data->board_id) ? $data->board_id: -1;
if (!isset($data->trigger) || !isset($data->type) || $action->trigger = isset($data->trigger) ? $data->trigger : -1;
!isset($data->board_id)) { $action->source_id = isset($data->source_id) ? $data->source_id: -1;
return false; $action->type = isset($data->type) ? $data->type : '';
} $action->change_to = isset($data->change_to) ? $data->change_to: -1;
$action->board_id = isset($data->board_id) ? $data->board_id: -1;
return true; if (!isset($data->trigger) || !isset($data->type) ||
!isset($data->board_id)) {
return false;
} }
public static function LoadBoard(&$board, $json) { return true;
$data = json_decode($json); }
$board->name = isset($data->name) ? $data->name : ''; public static function LoadBoard(&$board, $json) {
$board->is_active = isset($data->is_active) ? $data->is_active : false; $data = json_decode($json);
if (isset($data->categories)) { $board->name = isset($data->name) ? $data->name : '';
self::updateObjectList('category', 'LoadCategory', $board->is_active = isset($data->is_active) ? $data->is_active : false;
$board->xownCategoryList, $data->categories);
}
if (isset($data->issue_trackers)) { if (isset($data->categories)) {
self::updateObjectList('issuetracker', 'LoadIssueTracker', self::updateObjectList('category', 'LoadCategory',
$board->xownIssueTrackerList, $board->xownCategoryList, $data->categories);
$data->issue_trackers);
}
if (isset($data->columns)) {
self::updateObjectList('column', 'LoadColumn',
$board->xownColumnList, $data->columns);
}
// Users do not get deleted when removed from a board
if (isset($data->users)) {
$board->sharedUserList = [];
foreach ($data->users as $userData) {
$user = R::load('user', $userData->id);
if ((int)$user->id) {
$board->sharedUserList[] = $user;
}
}
}
if (!isset($data->name) || !isset($data->is_active) ||
!isset($data->categories) || !isset($data->columns) ||
!isset($data->issue_trackers) || !isset($data->users)) {
return false;
}
return true;
} }
public static function LoadCategory(&$category, $json) { if (isset($data->issue_trackers)) {
$data = json_decode($json); self::updateObjectList('issuetracker', 'LoadIssueTracker',
$board->xownIssueTrackerList,
$category->name = isset($data->name) ? $data->name : ''; $data->issue_trackers);
$category->default_task_color = isset($data->default_task_color)
? $data->default_task_color : '';
$category->board_id = isset($data->board_id) ? $data->board_id : -1;
if (!isset($data->name) || !isset($data->default_task_color) ||
!isset($data->board_id)) {
return false;
}
return true;
} }
public static function LoadColumn(&$column, $json) { if (isset($data->columns)) {
$data = json_decode($json); self::updateObjectList('column', 'LoadColumn',
$board->xownColumnList, $data->columns);
$column->name = isset($data->name) ? $data->name : '';
$column->position = isset($data->position) ? $data->position : -1;
$column->board_id = isset($data->board_id) ? $data->board_id : -1;
$column->task_limit = isset($data->task_limit) ? $data->task_limit : 0;
if (isset($data->tasks)) {
self::updateObjectList('task', 'LoadTask',
$column->xownTaskList, $data->tasks);
}
if (!isset($data->name) || !isset($data->position) ||
!isset($data->board_id)) {
return false;
}
return true;
} }
public static function LoadComment(&$comment, $json) { // Users do not get deleted when removed from a board
$data = json_decode($json); if (isset($data->users)) {
$board->sharedUserList = [];
$comment->text = isset($data->text) ? $data->text : ''; foreach ($data->users as $userData) {
$comment->user_id = isset($data->user_id) ? $data->user_id : -1; $user = R::load('user', $userData->id);
$comment->task_id = isset($data->task_id) ? $data->task_id : -1;
$comment->timestamp = isset($data->timestamp) ? $data->timestamp : -1;
$comment->is_edited = isset($data->is_edited) ? $data->is_edited : false;
if (!isset($data->text) || !isset($data->user_id) || if ((int)$user->id) {
!isset($data->task_id) || !isset($data->timestamp)) { $board->sharedUserList[] = $user;
return false;
} }
}
return true;
} }
public static function LoadIssueTracker(&$tracker, $json) { if (!isset($data->name) || !isset($data->is_active) ||
$data = json_decode($json); !isset($data->categories) || !isset($data->columns) ||
!isset($data->issue_trackers) || !isset($data->users)) {
$tracker->url = isset($data->url) ? $data->url : ''; return false;
$tracker->regex = isset($data->regex) ? $data->regex : '';
$tracker->board_id = isset($data->board_id) ? $data->board_id : -1;
if (!isset($data->url) || !isset($data->regex) ||
!isset($data->board_id)) {
return false;
}
return true;
} }
public static function LoadTask(&$task, $json) { return true;
$data = json_decode($json); }
$task->title = isset($data->title) ? $data->title : ''; public static function LoadCategory(&$category, $json) {
$task->description = isset($data->description) $data = json_decode($json);
? $data->description : '';
$task->color = isset($data->color) ? $data->color : '';
$task->due_date = isset($data->due_date) ? $data->due_date : '';
$task->points = isset($data->points) ? $data->points : 0;
$task->position = isset($data->position) ? $data->position : -1;
$task->column_id = isset($data->column_id) ? $data->column_id : -1;
if (isset($data->comments)) { $category->name = isset($data->name) ? $data->name : '';
self::updateObjectList('comment', 'LoadComment', $category->default_task_color = isset($data->default_task_color)
$task->xownCommentList, $data->comments); ? $data->default_task_color : '';
} $category->board_id = isset($data->board_id) ? $data->board_id : -1;
if (isset($data->attachments)) { if (!isset($data->name) || !isset($data->default_task_color) ||
self::updateObjectList('attachment', 'LoadAttachment', !isset($data->board_id)) {
$task->xownAttachmentList, $data->attachments); return false;
}
if (isset($data->assignees)) {
$task->sharedUserList = [];
foreach ($data->assignees as $assignee) {
$user = R::load('user', $assignee->id);
if ((int)$user->id) {
$task->sharedUserList[] = $user;
}
}
}
if (isset($data->categories)) {
$task->sharedCategoryList = [];
foreach ($data->categories as $category) {
$cat = R::load('category', $category->id);
if ((int)$cat->id) {
$task->sharedCategoryList[] = $cat;
}
}
}
if (!isset($data->title) || !isset($data->position) ||
!isset($data->column_id)) {
return false;
}
return true;
} }
public static function LoadUser(&$user, $json) { return true;
$data = json_decode($json); }
$user->security_level = isset($data->security_level) public static function LoadColumn(&$column, $json) {
? $data->security_level : -1; $data = json_decode($json);
$user->username = isset($data->username) ? $data->username : '';
$user->email = isset($data->email) ? $data->email : '';
$user->default_board_id = isset($data->default_board_id)
? $data->default_board_id : -1;
$user->user_option_id = isset($data->user_option_id)
? $data->user_option_id : -1;
$user->last_login = isset($data->last_login) ? $data->last_login : '';
$user->password_hash = isset($data->password_hash)
? $data->password_hash : '';
if (!isset($data->security_level) || !isset($data->username)) { $column->name = isset($data->name) ? $data->name : '';
return false; $column->position = isset($data->position) ? $data->position : -1;
} $column->board_id = isset($data->board_id) ? $data->board_id : -1;
$column->task_limit = isset($data->task_limit) ? $data->task_limit : 0;
return true; if (isset($data->tasks)) {
self::updateObjectList('task', 'LoadTask',
$column->xownTaskList, $data->tasks);
} }
public static function LoadUserOption(&$opts, $json) { if (!isset($data->name) || !isset($data->position) ||
$data = json_decode($json); !isset($data->board_id)) {
return false;
$opts->new_tasks_at_bottom = isset($data->new_tasks_at_bottom)
? (boolean)$data->new_tasks_at_bottom : true;
$opts->show_animations = isset($data->show_animations)
? (boolean)$data->show_animations : true;
$opts->show_assignee = isset($data->show_assignee)
? (boolean)$data->show_assignee : true;
$opts->multiple_tasks_per_row = isset($data->multiple_tasks_per_row)
? (boolean)$data->multiple_tasks_per_row : false;
$opts->language = isset($data->language)
? $data->language : '';
if (!isset($data->new_tasks_at_bottom) ||
!isset($data->show_animations) || !isset($data->show_assignee) ||
!isset($data->multiple_tasks_per_row) || !isset($data->language)) {
return false;
}
return true;
} }
private static function removeObjectsNotInData($type, &$dataList, &$objectList) { return true;
$dataIds = []; }
foreach ($dataList as $data) { public static function LoadComment(&$comment, $json) {
if (isset($data->id)) { $data = json_decode($json);
$dataIds[] = (int)$data->id;
}
}
foreach ($objectList as $existing) { $comment->text = isset($data->text) ? $data->text : '';
if (!in_array((int)$existing->id, $dataIds)) { $comment->user_id = isset($data->user_id) ? $data->user_id : -1;
$remove = R::load($type, $existing->id); $comment->task_id = isset($data->task_id) ? $data->task_id : -1;
R::trash($remove); $comment->timestamp = isset($data->timestamp) ? $data->timestamp : -1;
} $comment->is_edited = isset($data->is_edited) ? $data->is_edited : false;
}
if (!isset($data->text) || !isset($data->user_id) ||
!isset($data->task_id) || !isset($data->timestamp)) {
return false;
} }
private static function loadObjectsFromData($type, $loadFunc, &$dataList, return true;
&$objectList) { }
foreach ($dataList as $obj) {
$object = R::load($type, (isset($obj->id) ? $obj->id : 0));
call_user_func_array(array(__CLASS__, $loadFunc), public static function LoadIssueTracker(&$tracker, $json) {
array(&$object, json_encode($obj))); $data = json_decode($json);
$objectList[] = $object;
} $tracker->url = isset($data->url) ? $data->url : '';
$tracker->regex = isset($data->regex) ? $data->regex : '';
$tracker->board_id = isset($data->board_id) ? $data->board_id : -1;
if (!isset($data->url) || !isset($data->regex) ||
!isset($data->board_id)) {
return false;
} }
private static function updateObjectList($type, $loadFunc, return true;
&$objectList = [], }
&$dataList = []) {
if (count($objectList) && count($dataList)) {
self::removeObjectsNotInData($type, $dataList, $objectList);
}
if (count($dataList)) { public static function LoadTask(&$task, $json) {
self::loadObjectsFromData($type, $loadFunc, $dataList, $objectList); $data = json_decode($json);
}
// Remove all objects from existing boardlist when none in datalist $task->title = isset($data->title) ? $data->title : '';
if (!count($dataList) && count($objectList)) { $task->description = isset($data->description)
foreach ($objectList as $obj) { ? $data->description : '';
R::trash($obj); $task->color = isset($data->color) ? $data->color : '';
} $task->due_date = isset($data->due_date) ? $data->due_date : '';
} $task->points = isset($data->points) ? $data->points : 0;
$task->position = isset($data->position) ? $data->position : -1;
$task->column_id = isset($data->column_id) ? $data->column_id : -1;
if (isset($data->comments)) {
self::updateObjectList('comment', 'LoadComment',
$task->xownCommentList, $data->comments);
} }
if (isset($data->attachments)) {
self::updateObjectList('attachment', 'LoadAttachment',
$task->xownAttachmentList, $data->attachments);
}
if (isset($data->assignees)) {
$task->sharedUserList = [];
foreach ($data->assignees as $assignee) {
$user = R::load('user', $assignee->id);
if ((int)$user->id) {
$task->sharedUserList[] = $user;
}
}
}
if (isset($data->categories)) {
$task->sharedCategoryList = [];
foreach ($data->categories as $category) {
$cat = R::load('category', $category->id);
if ((int)$cat->id) {
$task->sharedCategoryList[] = $cat;
}
}
}
if (!isset($data->title) || !isset($data->position) ||
!isset($data->column_id)) {
return false;
}
return true;
}
public static function LoadUser(&$user, $json) {
$data = json_decode($json);
$user->security_level = isset($data->security_level)
? $data->security_level : -1;
$user->username = isset($data->username) ? $data->username : '';
$user->email = isset($data->email) ? $data->email : '';
$user->default_board_id = isset($data->default_board_id)
? $data->default_board_id : -1;
$user->user_option_id = isset($data->user_option_id)
? $data->user_option_id : -1;
$user->last_login = isset($data->last_login) ? $data->last_login : '';
$user->password_hash = isset($data->password_hash)
? $data->password_hash : '';
if (!isset($data->security_level) || !isset($data->username)) {
return false;
}
return true;
}
public static function LoadUserOption(&$opts, $json) {
$data = json_decode($json);
$opts->new_tasks_at_bottom = isset($data->new_tasks_at_bottom)
? (boolean)$data->new_tasks_at_bottom : true;
$opts->show_animations = isset($data->show_animations)
? (boolean)$data->show_animations : true;
$opts->show_assignee = isset($data->show_assignee)
? (boolean)$data->show_assignee : true;
$opts->multiple_tasks_per_row = isset($data->multiple_tasks_per_row)
? (boolean)$data->multiple_tasks_per_row : false;
$opts->language = isset($data->language)
? $data->language : '';
if (!isset($data->new_tasks_at_bottom) ||
!isset($data->show_animations) || !isset($data->show_assignee) ||
!isset($data->multiple_tasks_per_row) || !isset($data->language)) {
return false;
}
return true;
}
private static function removeObjectsNotInData($type, &$dataList, &$objectList) {
$dataIds = [];
foreach ($dataList as $data) {
if (isset($data->id)) {
$dataIds[] = (int)$data->id;
}
}
foreach ($objectList as $existing) {
if (!in_array((int)$existing->id, $dataIds)) {
$remove = R::load($type, $existing->id);
R::trash($remove);
}
}
}
private static function loadObjectsFromData($type, $loadFunc, &$dataList,
&$objectList) {
foreach ($dataList as $obj) {
$object = R::load($type, (isset($obj->id) ? $obj->id : 0));
call_user_func_array(array(__CLASS__, $loadFunc),
array(&$object, json_encode($obj)));
$objectList[] = $object;
}
}
private static function updateObjectList($type, $loadFunc,
&$objectList = [],
&$dataList = []) {
if (count($objectList) && count($dataList)) {
self::removeObjectsNotInData($type, $dataList, $objectList);
}
if (count($dataList)) {
self::loadObjectsFromData($type, $loadFunc, $dataList, $objectList);
}
// Remove all objects from existing boardlist when none in datalist
if (!count($dataList) && count($objectList)) {
foreach ($objectList as $obj) {
R::trash($obj);
}
}
}
} }

View File

@ -2,19 +2,19 @@
use RedBeanPHP\R; use RedBeanPHP\R;
class DbLogger { class DbLogger {
public static function logChange($user_id, $log_text, $before, public static function logChange($user_id, $log_text, $before,
$after, $item_type, $item_id) { $after, $item_type, $item_id) {
$activity = R::dispense('activity'); $activity = R::dispense('activity');
$activity->user_id = $user_id; $activity->user_id = $user_id;
$activity->log_text = $log_text; $activity->log_text = $log_text;
$activity->before = $before; $activity->before = $before;
$activity->after = $after; $activity->after = $after;
$activity->item_type = $item_type; $activity->item_type = $item_type;
$activity->item_id = $item_id; $activity->item_id = $item_id;
$activity->timestamp = time(); $activity->timestamp = time();
R::store($activity); R::store($activity);
} }
} }

View File

@ -2,9 +2,9 @@
use MyCLabs\Enum\Enum; use MyCLabs\Enum\Enum;
final class SecurityLevel extends Enum { final class SecurityLevel extends Enum {
const ADMIN = 1; const ADMIN = 1;
const BOARD_ADMIN = 2; const BOARD_ADMIN = 2;
const USER = 3; const USER = 3;
const UNPRIVILEGED = 4; const UNPRIVILEGED = 4;
} }

View File

@ -132,6 +132,7 @@ export class BoardDisplayComponent implements OnInit, OnDestroy, AfterContentIni
} }
}); });
/* istanbul ignore next */
this.dragula.dropModel('tasks-bag').subscribe((value: any) => { this.dragula.dropModel('tasks-bag').subscribe((value: any) => {
const taskId = +value[1].id; const taskId = +value[1].id;
const toColumnId = +value[2].parentNode.id; const toColumnId = +value[2].parentNode.id;
@ -298,6 +299,7 @@ export class BoardDisplayComponent implements OnInit, OnDestroy, AfterContentIni
} }
} }
/* istanbul ignore next */
private changeTaskColumn(taskId: number, toColumnId: number) { private changeTaskColumn(taskId: number, toColumnId: number) {
const column = this.activeBoard.columns const column = this.activeBoard.columns
.find(col => col.id === toColumnId); .find(col => col.id === toColumnId);

View File

@ -134,6 +134,7 @@ export class BoardService {
); );
} }
/* istanbul ignore next */
uploadAttachment(attachment: Attachment, data: FormData): Observable<ApiResponse> { uploadAttachment(attachment: Attachment, data: FormData): Observable<ApiResponse> {
const headers = new HttpHeaders(); const headers = new HttpHeaders();
const options = { headers, params: new HttpParams() }; const options = { headers, params: new HttpParams() };
@ -151,7 +152,10 @@ export class BoardService {
} }
private defaultCallback = (err: any, text: string) => { private defaultCallback = (err: any, text: string) => {
console.log('default', err, text); if (err) {
return '';
}
return text; return text;
} }

View File

@ -281,10 +281,12 @@ export class ColumnDisplayComponent implements OnInit, OnDestroy {
}); });
} }
/* istanbul ignore next */
fileChange(file: File) { fileChange(file: File) {
this.fileUpload = file; this.fileUpload = file;
} }
/* istanbul ignore next */
uploadFile() { uploadFile() {
if (!this.fileUpload) { if (!this.fileUpload) {
this.notes.add({ type: 'error', text: this.strings.boards_taskNoFileError }); this.notes.add({ type: 'error', text: this.strings.boards_taskNoFileError });

View File

@ -1,5 +1,6 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
/* istanbul ignore next */
@Component({ @Component({
selector: 'tb-calendar', selector: 'tb-calendar',
templateUrl: './calendar.component.html' templateUrl: './calendar.component.html'

View File

@ -1,6 +1,7 @@
import { Component, Input, AfterViewInit } from '@angular/core'; import { Component, Input, AfterViewInit } from '@angular/core';
import * as Chartist from 'chartist'; import * as Chartist from 'chartist';
/* istanbul ignore next */
@Component({ @Component({
selector: 'tb-charts', selector: 'tb-charts',
templateUrl: './charts.component.html' templateUrl: './charts.component.html'

View File

@ -4,6 +4,7 @@ import { Title } from '@angular/platform-browser';
// import { Charts } from './charts/charts.component'; // import { Charts } from './charts/charts.component';
// import { Calendar } from './calendar/calendar.component'; // import { Calendar } from './calendar/calendar.component';
/* istanbul ignore next */
@Component({ @Component({
selector: 'tb-dashboard', selector: 'tb-dashboard',
templateUrl: './dashboard.component.html' templateUrl: './dashboard.component.html'

View File

@ -4,182 +4,182 @@ use Firebase\JWT\JWT;
class AppMock { class AppMock {
public function getContainer() { public function getContainer() {
return new ContainerMock(); return new ContainerMock();
} }
} }
$app = new AppMock(); $app = new AppMock();
class DataMock { class DataMock {
public static function GetJwt($userId = 1) { public static function GetJwt($userId = 1) {
Auth::CreateJwtSigningKey(); Auth::CreateJwtSigningKey();
$key = R::load('jwt', 1); $key = R::load('jwt', 1);
$jwt = JWT::encode(array( $jwt = JWT::encode(array(
'exp' => time() + (60 * 30), // 30 minutes 'exp' => time() + (60 * 30), // 30 minutes
'uid' => $userId, 'uid' => $userId,
'mul' => 1 'mul' => 1
), $key->secret); ), $key->secret);
$user = R::load('user', $userId); $user = R::load('user', $userId);
$user->active_token = $jwt; $user->active_token = $jwt;
R::store($user); R::store($user);
return $jwt; return $jwt;
} }
public static function CreateStandardUser() { public static function CreateStandardUser() {
$user = R::dispense('user'); $user = R::dispense('user');
self::setUserDefaults($user); self::setUserDefaults($user);
R::store($user); R::store($user);
} }
public static function CreateBoardAdminUser() { public static function CreateBoardAdminUser() {
$user = R::dispense('user'); $user = R::dispense('user');
self::setUserDefaults($user); self::setUserDefaults($user);
$user->username = 'boardadmin'; $user->username = 'boardadmin';
$user->security_level = SecurityLevel::BOARD_ADMIN; $user->security_level = SecurityLevel::BOARD_ADMIN;
R::store($user); R::store($user);
} }
public static function CreateUnprivilegedUser() { public static function CreateUnprivilegedUser() {
$user = R::dispense('user'); $user = R::dispense('user');
self::setUserDefaults($user); self::setUserDefaults($user);
$user->username = 'badtester'; $user->username = 'badtester';
$user->security_level = SecurityLevel::UNPRIVILEGED; $user->security_level = SecurityLevel::UNPRIVILEGED;
R::store($user); R::store($user);
} }
public static function CreateBoard() { public static function CreateBoard() {
$board = R::dispense('board'); $board = R::dispense('board');
$board->name = 'test'; $board->name = 'test';
$board->is_active = true; $board->is_active = true;
} }
private static function setUserDefaults(&$user) { private static function setUserDefaults(&$user) {
$user->username = 'tester'; $user->username = 'tester';
$user->security_level = SecurityLevel::USER; $user->security_level = SecurityLevel::USER;
$user->password_hash = 'hashpass1234'; $user->password_hash = 'hashpass1234';
$user->email = 'user@example.com'; $user->email = 'user@example.com';
$user->default_board_id = 0; $user->default_board_id = 0;
$user->user_option_id = 0; $user->user_option_id = 0;
$user->last_login = 123456789; $user->last_login = 123456789;
$user->active_token = ''; $user->active_token = '';
} }
} }
class LoggerMock { class LoggerMock {
public function addInfo() { public function addInfo() {
} }
public function addError() { public function addError() {
// Uncomment to log errors to file // Uncomment to log errors to file
// The tests cover errors, so there will be plenty to sift through // The tests cover errors, so there will be plenty to sift through
// $msg = func_get_arg(0); // $msg = func_get_arg(0);
// $err = 'API ERROR: ' . $msg . PHP_EOL; // $err = 'API ERROR: ' . $msg . PHP_EOL;
// $objs = func_get_args(); // $objs = func_get_args();
// array_splice($objs, 0, 1); // array_splice($objs, 0, 1);
// ob_start(); // ob_start();
// foreach($objs as $obj) { // foreach($objs as $obj) {
// var_dump($obj); // var_dump($obj);
// } // }
// $strings = ob_get_clean(); // $strings = ob_get_clean();
// file_put_contents('tests.log', [$err, $strings], FILE_APPEND); // file_put_contents('tests.log', [$err, $strings], FILE_APPEND);
} }
} }
class ContainerMock { class ContainerMock {
public function get() { public function get() {
return new LoggerMock(); return new LoggerMock();
} }
} }
class RequestMock { class RequestMock {
public $invalidPayload = false; public $invalidPayload = false;
public $payload = null; public $payload = null;
public $hasHeader = true; public $hasHeader = true;
public $header = null; public $header = null;
public $throwInHeader = false; public $throwInHeader = false;
public function getBody() { public function getBody() {
if ($this->invalidPayload) { if ($this->invalidPayload) {
return '{}'; return '{}';
}
if ($this->payload) {
return json_encode($this->payload);
}
return '';
} }
public function hasHeader() { if ($this->payload) {
return $this->hasHeader; return json_encode($this->payload);
} }
public function getHeader($header) { return '';
if ($this->throwInHeader) { }
throw new Exception();
}
if ($this->header) { public function hasHeader() {
return $this->header; return $this->hasHeader;
} }
return $header; public function getHeader($header) {
if ($this->throwInHeader) {
throw new Exception();
} }
if ($this->header) {
return $this->header;
}
return $header;
}
} }
class ResponseMock { class ResponseMock {
public $status = 200; public $status = 200;
public $body; public $body;
public function __construct() { public function __construct() {
$this->body = new RequestBodyMock(); $this->body = new RequestBodyMock();
} }
public function withJson($apiJson) { public function withJson($apiJson) {
return $apiJson; return $apiJson;
} }
public function withStatus($status) { public function withStatus($status) {
$this->status = $status; $this->status = $status;
return $this; return $this;
} }
public function getStatusCode() { public function getStatusCode() {
return $this->status; return $this->status;
} }
public function getBody() { public function getBody() {
return $this->body; return $this->body;
} }
} }
class RequestBodyMock { class RequestBodyMock {
public $data; public $data;
public function __toString() { public function __toString() {
return $this->data; return $this->data;
} }
public function write($string) { public function write($string) {
$this->data = $string; $this->data = $string;
} }
} }

View File

@ -3,96 +3,96 @@ require_once __DIR__ . '/../Mocks.php';
use RedBeanPHP\R; use RedBeanPHP\R;
class ActivityTest extends PHPUnit\Framework\TestCase { class ActivityTest extends PHPUnit\Framework\TestCase {
private $activity; private $activity;
public static function setupBeforeClass() { public static function setUpBeforeClass(): void {
try { try {
R::setup('sqlite:tests.db'); R::setup('sqlite:tests.db');
} catch (Exception $ex) { } catch (Exception $ex) {
}
} }
}
public function setUp() { public function setUp(): void {
R::nuke(); R::nuke();
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$this->activity = new Activity(new ContainerMock()); $this->activity = new Activity(new ContainerMock());
} }
public function testGetActivityInvalid() { public function testGetActivityInvalid() {
$request = new RequestMock(); $request = new RequestMock();
$request->hasHeader = false; $request->hasHeader = false;
$args = []; $args = [];
$args['type'] = 'task'; $args['type'] = 'task';
$args['id'] = 1; $args['id'] = 1;
$actual = $this->activity->getActivity($request, $actual = $this->activity->getActivity($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('error', $actual->alerts[0]['type']); $this->assertEquals('error', $actual->alerts[0]['type']);
} }
public function testGetActivityForbidden() { public function testGetActivityForbidden() {
$this->setupTaskActivity(); $this->setupTaskActivity();
$args = []; $args = [];
$args['type'] = 'task'; $args['type'] = 'task';
$args['id'] = 1; $args['id'] = 1;
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->activity->getActivity($request, $actual = $this->activity->getActivity($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $actual->alerts[0]['text']); $this->assertEquals('Access restricted.', $actual->alerts[0]['text']);
} }
public function testGetActivityForTask() { public function testGetActivityForTask() {
$this->setupTaskActivity(); $this->setupTaskActivity();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$args = []; $args = [];
$args['type'] = 'task'; $args['type'] = 'task';
$args['id'] = 1; $args['id'] = 1;
$actual = $this->activity->getActivity($request, $actual = $this->activity->getActivity($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$this->assertEquals(3, count($actual->data[1])); $this->assertEquals(3, count($actual->data[1]));
} }
private function setupTaskActivity() { private function setupTaskActivity() {
$task = R::dispense('task'); $task = R::dispense('task');
$comment = R::dispense('comment'); $comment = R::dispense('comment');
$attachment = R::dispense('attachment'); $attachment = R::dispense('attachment');
$task->ownComment[] = $comment; $task->ownComment[] = $comment;
$task->ownAttachment[] = $attachment; $task->ownAttachment[] = $attachment;
R::store($task); R::store($task);
$activity = R::dispense('activity'); $activity = R::dispense('activity');
$activity->item_type = 'task'; $activity->item_type = 'task';
$activity->item_id = 1; $activity->item_id = 1;
$activity->log_text = 'test change'; $activity->log_text = 'test change';
$activity->timestamp = time(); $activity->timestamp = time();
R::store($activity); R::store($activity);
$activity = R::dispense('activity'); $activity = R::dispense('activity');
$activity->item_type = 'task'; $activity->item_type = 'task';
$activity->item_id = 1; $activity->item_id = 1;
$activity->log_text = 'test change'; $activity->log_text = 'test change';
$activity->timestamp = time(); $activity->timestamp = time();
R::store($activity); R::store($activity);
$activity = R::dispense('activity'); $activity = R::dispense('activity');
$activity->item_type = 'task'; $activity->item_type = 'task';
$activity->item_id = 1; $activity->item_id = 1;
$activity->log_text = 'test change'; $activity->log_text = 'test change';
$activity->timestamp = time() + 10; $activity->timestamp = time() + 10;
R::store($activity); R::store($activity);
} }
} }

View File

@ -3,231 +3,231 @@ require_once __DIR__ . '/../Mocks.php';
use RedBeanPHP\R; use RedBeanPHP\R;
class AttachmentsTest extends PHPUnit\Framework\TestCase { class AttachmentsTest extends PHPUnit\Framework\TestCase {
private $attachments; private $attachments;
public static function setupBeforeClass() { public static function setUpBeforeClass(): void {
try { try {
R::setup('sqlite:tests.db'); R::setup('sqlite:tests.db');
} catch (Exception $ex) { } catch (Exception $ex) {
}
} }
}
public function setUp() { public function setUp(): void {
R::nuke(); R::nuke();
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$this->attachments = new Attachments(new ContainerMock()); $this->attachments = new Attachments(new ContainerMock());
} }
public function testGetAttachment() { public function testGetAttachment() {
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$actual = $this->attachments->getAttachment($request, $actual = $this->attachments->getAttachment($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('No attachment found for ID 1.', $this->assertEquals('No attachment found for ID 1.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
$this->createAttachment(); $this->createAttachment();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$this->attachments = new Attachments(new ContainerMock()); $this->attachments = new Attachments(new ContainerMock());
$actual = $this->attachments->getAttachment($request, $actual = $this->attachments->getAttachment($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$this->assertEquals(2, count($actual->data)); $this->assertEquals(2, count($actual->data));
} }
public function testGetAttachmentInvalid() { public function testGetAttachmentInvalid() {
$request = new RequestMock(); $request = new RequestMock();
$request->hasHeader = false; $request->hasHeader = false;
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$actual = $this->attachments->getAttachment($request, $actual = $this->attachments->getAttachment($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('error', $actual->alerts[0]['type']); $this->assertEquals('error', $actual->alerts[0]['type']);
} }
public function testGetAttachmentForbidden() { public function testGetAttachmentForbidden() {
$this->createAttachment(); $this->createAttachment();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$actual = $this->attachments->getAttachment(new RequestMock(), $actual = $this->attachments->getAttachment(new RequestMock(),
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('error', $actual->alerts[0]['type']); $this->assertEquals('error', $actual->alerts[0]['type']);
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$this->attachments = new Attachments(new ContainerMock()); $this->attachments = new Attachments(new ContainerMock());
$actual = $this->attachments->getAttachment($request, $actual = $this->attachments->getAttachment($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddAttachment() { public function testAddAttachment() {
$task = R::dispense('task'); $task = R::dispense('task');
R::store($task); R::store($task);
$data = new stdClass(); $data = new stdClass();
$data->filename = 'test'; $data->filename = 'test';
$data->name = 'test.png'; $data->name = 'test.png';
$data->type = 'image'; $data->type = 'image';
$data->user_id = 1; $data->user_id = 1;
$data->task_id = 1; $data->task_id = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $data; $request->payload = $data;
$actual = $this->attachments->addAttachment($request, $actual = $this->attachments->addAttachment($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Attachment added.', $actual->alerts[0]['text']); $this->assertEquals('Attachment added.', $actual->alerts[0]['text']);
} }
public function testAddAttachmentInvalid() { public function testAddAttachmentInvalid() {
$request = new RequestMock(); $request = new RequestMock();
$request->invalidPayload = true; $request->invalidPayload = true;
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->attachments->addAttachment($request, $actual = $this->attachments->addAttachment($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
$this->assertEquals('error', $actual->alerts[0]['type']); $this->assertEquals('error', $actual->alerts[0]['type']);
} }
public function testAddAttachmentForbidden() { public function testAddAttachmentForbidden() {
$task = R::dispense('task'); $task = R::dispense('task');
R::store($task); R::store($task);
$attachment = new stdClass(); $attachment = new stdClass();
$attachment->filename = "test"; $attachment->filename = "test";
$attachment->name = 'test.png'; $attachment->name = 'test.png';
$attachment->type = 'image'; $attachment->type = 'image';
$attachment->user_id = 1; $attachment->user_id = 1;
$attachment->task_id = 1; $attachment->task_id = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $attachment; $request->payload = $attachment;
$actual = $this->attachments->addAttachment($request, $actual = $this->attachments->addAttachment($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('error', $actual->alerts[0]['type']); $this->assertEquals('error', $actual->alerts[0]['type']);
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$this->attachments = new Attachments(new ContainerMock()); $this->attachments = new Attachments(new ContainerMock());
$actual = $this->attachments->addAttachment($request, $actual = $this->attachments->addAttachment($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemoveAttachment() { public function testRemoveAttachment() {
$this->createAttachment(); $this->createAttachment();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->attachments->removeAttachment($request, $actual = $this->attachments->removeAttachment($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Attachment file.png removed.', $this->assertEquals('Attachment file.png removed.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemoveUnprivileged() { public function testRemoveUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$this->createAttachment(); $this->createAttachment();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$actual = $this->attachments->removeAttachment($request, $actual = $this->attachments->removeAttachment($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemoveAttachmentUserSecurity() { public function testRemoveAttachmentUserSecurity() {
$this->createAttachment(); $this->createAttachment();
DataMock::CreateStandardUser(); DataMock::CreateStandardUser();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->attachments->removeAttachment($request, $actual = $this->attachments->removeAttachment($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('You do not have sufficient permissions to ' . $this->assertEquals('You do not have sufficient permissions to ' .
'remove this attachment.', $actual->alerts[0]['text']); 'remove this attachment.', $actual->alerts[0]['text']);
} }
public function testRemoveAttachmentForbidden() { public function testRemoveAttachmentForbidden() {
$this->createAttachment(); $this->createAttachment();
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->attachments->removeAttachment($request, $actual = $this->attachments->removeAttachment($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemoveBadAttachment() { public function testRemoveBadAttachment() {
$this->createAttachment(); $this->createAttachment();
$args = []; $args = [];
$args['id'] = 2; // No such attachment $args['id'] = 2; // No such attachment
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->attachments->removeAttachment($request, $actual = $this->attachments->removeAttachment($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Error removing attachment. ' . $this->assertEquals('Error removing attachment. ' .
'No attachment found for ID 2.', $actual->alerts[0]['text']); 'No attachment found for ID 2.', $actual->alerts[0]['text']);
} }
private function createAttachment() { private function createAttachment() {
$board = R::dispense('board'); $board = R::dispense('board');
$column = R::dispense('column'); $column = R::dispense('column');
$task = R::dispense('task'); $task = R::dispense('task');
$attachment = R::dispense('attachment'); $attachment = R::dispense('attachment');
$attachment->name = 'file.png'; $attachment->name = 'file.png';
$attachment->user_id = 1; $attachment->user_id = 1;
$task->xownAttachmentList[] = $attachment; $task->xownAttachmentList[] = $attachment;
$column->xownTaskList[] = $task; $column->xownTaskList[] = $task;
$board->xownColumnList[] = $column; $board->xownColumnList[] = $column;
R::store($board); R::store($board);
} }
} }

View File

@ -3,275 +3,275 @@ use RedBeanPHP\R;
use Firebase\JWT\JWT; use Firebase\JWT\JWT;
class AuthTest extends PHPUnit\Framework\TestCase { class AuthTest extends PHPUnit\Framework\TestCase {
private $auth; private $auth;
public static function setupBeforeClass() { public static function setUpBeforeClass(): void {
try { try {
R::setup('sqlite:tests.db'); R::setup('sqlite:tests.db');
} catch (Exception $ex) { } catch (Exception $ex) {
}
} }
}
public function setUp() { public function setUp(): void {
R::nuke(); R::nuke();
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
} }
public function testHasBoardAccess() { public function testHasBoardAccess() {
$user = R::dispense('user'); $user = R::dispense('user');
$board = R::dispense('board'); $board = R::dispense('board');
$board->sharedUserList[] = $user; $board->sharedUserList[] = $user;
R::store($board); R::store($board);
$hasAccess = Auth::HasBoardAccess(new RequestMock(), 1, 1); $hasAccess = Auth::HasBoardAccess(new RequestMock(), 1, 1);
$this->assertEquals(true, $hasAccess); $this->assertEquals(true, $hasAccess);
$user->security_level = SecurityLevel::ADMIN; $user->security_level = SecurityLevel::ADMIN;
R::store($user); R::store($user);
$hasAccess = Auth::HasBoardAccess(new RequestMock(), 1, 1); $hasAccess = Auth::HasBoardAccess(new RequestMock(), 1, 1);
$this->assertEquals(true, $hasAccess); $this->assertEquals(true, $hasAccess);
} }
public function testCreateInitialAdmin() { public function testCreateInitialAdmin() {
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$admin = R::load('user', 1); $admin = R::load('user', 1);
$this->assertEquals(1, (int) $admin->id); $this->assertEquals(1, (int) $admin->id);
$this->assertEquals('admin', $admin->username); $this->assertEquals('admin', $admin->username);
// Call again to verify only one is created // Call again to verify only one is created
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$this->assertEquals(1, R::count('user')); $this->assertEquals(1, R::count('user'));
} }
public function testCreateJwtSigningKey() { public function testCreateJwtSigningKey() {
Auth::CreateJwtSigningKey(); Auth::CreateJwtSigningKey();
$jwt = R::load('jwt', 1); $jwt = R::load('jwt', 1);
$this->assertEquals(1, (int) $jwt->id); $this->assertEquals(1, (int) $jwt->id);
$this->assertTrue(strlen($jwt->secret) > 1); $this->assertTrue(strlen($jwt->secret) > 1);
// Call again to verify only one is created // Call again to verify only one is created
Auth::CreateJwtSigningKey(); Auth::CreateJwtSigningKey();
$this->assertEquals(1, R::count('jwt')); $this->assertEquals(1, R::count('jwt'));
} }
public function testValidateTokenFailures() { public function testValidateTokenFailures() {
$request = new RequestMock(); $request = new RequestMock();
$request->hasHeader = false; $request->hasHeader = false;
$actual = Auth::ValidateToken($request, new ResponseMock(), null); $actual = Auth::ValidateToken($request, new ResponseMock(), null);
$this->assertEquals(400, $actual->status); $this->assertEquals(400, $actual->status);
$actual = Auth::ValidateToken(new RequestMock(), $actual = Auth::ValidateToken(new RequestMock(),
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals(401, $actual->status); $this->assertEquals(401, $actual->status);
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$user = R::load('user', 1); $user = R::load('user', 1);
$user->active_token = 'whatever'; $user->active_token = 'whatever';
R::store($user); R::store($user);
$actual = Auth::ValidateToken($request, new ResponseMock(), null); $actual = Auth::ValidateToken($request, new ResponseMock(), null);
$this->assertEquals(401, $actual->status); $this->assertEquals(401, $actual->status);
} }
public function testValidateToken() { public function testValidateToken() {
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
Auth::CreateJwtSigningKey(); Auth::CreateJwtSigningKey();
$jwtKey = R::load('jwt', 1); $jwtKey = R::load('jwt', 1);
$admin = R::load('user', 1); $admin = R::load('user', 1);
$token = JWT::encode(array( $token = JWT::encode(array(
'exp' => time() + 600, 'exp' => time() + 600,
'uid' => 1, 'uid' => 1,
'mul' => 1 'mul' => 1
), $jwtKey->secret); ), $jwtKey->secret);
$admin->active_token = $token; $admin->active_token = $token;
R::store($admin); R::store($admin);
$response = new ResponseMock(); $response = new ResponseMock();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [$token]; $request->header = [$token];
Auth::ValidateToken($request, $response, Auth::ValidateToken($request, $response,
new ContainerMock()); new ContainerMock());
$this->assertEquals(200, $response->status); $this->assertEquals(200, $response->status);
} }
public function testGetUserId() { public function testGetUserId() {
Auth::CreateJwtSigningKey(); Auth::CreateJwtSigningKey();
$jwtKey = R::load('jwt', 1); $jwtKey = R::load('jwt', 1);
$token = JWT::encode(array( $token = JWT::encode(array(
'exp' => time() + 600, 'exp' => time() + 600,
'uid' => 1, 'uid' => 1,
'mul' => 1 'mul' => 1
), $jwtKey->secret); ), $jwtKey->secret);
$request = new RequestMock(); $request = new RequestMock();
$request->header = [$token]; $request->header = [$token];
$actual = Auth::GetUserId($request); $actual = Auth::GetUserId($request);
$this->assertEquals(1, $actual); $this->assertEquals(1, $actual);
$request->throwInHeader = true; $request->throwInHeader = true;
$actual = Auth::GetUserId($request); $actual = Auth::GetUserId($request);
$this->assertEquals(-1, $actual); $this->assertEquals(-1, $actual);
} }
public function testLogin() { public function testLogin() {
$data = new stdClass(); $data = new stdClass();
$data->username = 'admin'; $data->username = 'admin';
$data->password = 'admin'; $data->password = 'admin';
$data->remember = false; $data->remember = false;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $data; $request->payload = $data;
$actual = $this->auth->login($request, new ResponseMock(), null); $actual = $this->auth->login($request, new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
Auth::CreateJwtSigningKey(); Auth::CreateJwtSigningKey();
$actual = $this->auth->login($request, new ResponseMock(), null); $actual = $this->auth->login($request, new ResponseMock(), null);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
$request->payload->password = 'asdf'; $request->payload->password = 'asdf';
$actual = $this->auth->login($request, new ResponseMock(), null); $actual = $this->auth->login($request, new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
public function testLogout() { public function testLogout() {
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$data = new stdClass(); $data = new stdClass();
$data->username = 'admin'; $data->username = 'admin';
$data->password = 'admin'; $data->password = 'admin';
$data->remember = false; $data->remember = false;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $data; $request->payload = $data;
$actual = $this->auth->login($request, new ResponseMock(), null); $actual = $this->auth->login($request, new ResponseMock(), null);
$jwt = $actual->data[0]; $jwt = $actual->data[0];
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
$request = new RequestMock(); $request = new RequestMock();
$request->header = [$jwt]; $request->header = [$jwt];
$actual = $this->auth->logout($request, new ResponseMock(), null); $actual = $this->auth->logout($request, new ResponseMock(), null);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
} }
public function testLogoutFailures() { public function testLogoutFailures() {
$actual = $this->auth->logout(new RequestMock(), $actual = $this->auth->logout(new RequestMock(),
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
$request = new RequestMock(); $request = new RequestMock();
$request->hasHeader = false; $request->hasHeader = false;
$actual = $this->auth->logout($request, new ResponseMock(), null); $actual = $this->auth->logout($request, new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
public function testAuthenticate() { public function testAuthenticate() {
$data = new stdClass(); $data = new stdClass();
$data->username = 'admin'; $data->username = 'admin';
$data->password = 'admin'; $data->password = 'admin';
$data->remember = false; $data->remember = false;
$collapsed = R::dispense('collapsed'); $collapsed = R::dispense('collapsed');
$collapsed->user_id = 1; $collapsed->user_id = 1;
$collapsed->column_id = 1; $collapsed->column_id = 1;
R::store($collapsed); R::store($collapsed);
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $data; $request->payload = $data;
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
Auth::CreateJwtSigningKey(); Auth::CreateJwtSigningKey();
$actual = $this->auth->login($request, new ResponseMock(), null); $actual = $this->auth->login($request, new ResponseMock(), null);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$jwt = $actual->data[0]; $jwt = $actual->data[0];
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
$request = new RequestMock(); $request = new RequestMock();
$request->header = [$jwt]; $request->header = [$jwt];
$actual = $this->auth->authenticate($request, new ResponseMock(), null); $actual = $this->auth->authenticate($request, new ResponseMock(), null);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
$request->hasHeader = false; $request->hasHeader = false;
$actual = $this->auth->authenticate($request, new ResponseMock(), null); $actual = $this->auth->authenticate($request, new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
$request = new RequestMock(); $request = new RequestMock();
$request->header = ['not a valid JWT']; $request->header = ['not a valid JWT'];
$actual = $this->auth->authenticate($request, new ResponseMock(), null); $actual = $this->auth->authenticate($request, new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
public function testRefreshToken() { public function testRefreshToken() {
$data = new stdClass(); $data = new stdClass();
$data->username = 'admin'; $data->username = 'admin';
$data->password = 'admin'; $data->password = 'admin';
$data->remember = false; $data->remember = false;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $data; $request->payload = $data;
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
Auth::CreateJwtSigningKey(); Auth::CreateJwtSigningKey();
$actual = $this->auth->login($request, new ResponseMock(), null); $actual = $this->auth->login($request, new ResponseMock(), null);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$jwt = $actual->data[0]; $jwt = $actual->data[0];
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
$request = new RequestMock(); $request = new RequestMock();
$request->header = [$jwt]; $request->header = [$jwt];
$actual = $this->auth->refreshToken($request, new ResponseMock(), null); $actual = $this->auth->refreshToken($request, new ResponseMock(), null);
$user = R::load('user', 1); $user = R::load('user', 1);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$this->assertEquals($user->active_token, $actual->data[0]); $this->assertEquals($user->active_token, $actual->data[0]);
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
$request->hasHeader = false; $request->hasHeader = false;
$actual = $this->auth->refreshToken($request, new ResponseMock(), null); $actual = $this->auth->refreshToken($request, new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
$this->auth = new Auth(new ContainerMock()); $this->auth = new Auth(new ContainerMock());
$request = new RequestMock(); $request = new RequestMock();
$request->header = ['not a valid JWT']; $request->header = ['not a valid JWT'];
$actual = $this->auth->refreshToken($request, new ResponseMock(), null); $actual = $this->auth->refreshToken($request, new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
} }

View File

@ -3,197 +3,197 @@ require_once __DIR__ . '/../Mocks.php';
use RedBeanPHP\R; use RedBeanPHP\R;
class AutoActionsTest extends PHPUnit\Framework\TestCase { class AutoActionsTest extends PHPUnit\Framework\TestCase {
private $actions; private $actions;
public static function setupBeforeClass() { public static function setUpBeforeClass(): void {
try { try {
R::setup('sqlite:tests.db'); R::setup('sqlite:tests.db');
} catch (Exception $ex) { } catch (Exception $ex) {
}
} }
}
public function setUp() { public function setUp(): void {
R::nuke(); R::nuke();
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$this->actions = new AutoActions(new ContainerMock()); $this->actions = new AutoActions(new ContainerMock());
} }
public function testGetAllActions() { public function testGetAllActions() {
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->actions->getAllActions($request, $actual = $this->actions->getAllActions($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('No automatic actions in database.', $this->assertEquals('No automatic actions in database.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
$this->actions = new AutoActions(new ContainerMock()); $this->actions = new AutoActions(new ContainerMock());
$this->createAutoAction(); $this->createAutoAction();
$actual = $this->actions->getAllActions($request, $actual = $this->actions->getAllActions($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals(1, count($actual->data[1])); $this->assertEquals(1, count($actual->data[1]));
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
DataMock::CreateStandardUser(); DataMock::CreateStandardUser();
$this->actions = new AutoActions(new ContainerMock()); $this->actions = new AutoActions(new ContainerMock());
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->actions->getAllActions($request, $actual = $this->actions->getAllActions($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals(0, count($actual->data[1])); $this->assertEquals(0, count($actual->data[1]));
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
public function testGetAllActionsUnprivileged() { public function testGetAllActionsUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->actions->getAllActions($request, $actual = $this->actions->getAllActions($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddAction() { public function testAddAction() {
$board = R::dispense('board'); $board = R::dispense('board');
R::store($board); R::store($board);
$data = $this->getDefaultAction(); $data = $this->getDefaultAction();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $data; $request->payload = $data;
$actual = $this->actions->addAction($request, $actual = $this->actions->addAction($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('success', $actual->alerts[0]['type']); $this->assertEquals('success', $actual->alerts[0]['type']);
} }
public function testAddActionUnprivileged() { public function testAddActionUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->actions->addAction($request, $actual = $this->actions->addAction($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddActionInvalid() { public function testAddActionInvalid() {
$request = new RequestMock(); $request = new RequestMock();
$request->invalidPayload = true; $request->invalidPayload = true;
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->actions->addAction($request, $actual = $this->actions->addAction($request,
new ResponseMock, null); new ResponseMock, null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
public function testAddActionForbidden() { public function testAddActionForbidden() {
$board = R::dispense('board'); $board = R::dispense('board');
R::store($board); R::store($board);
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$data = $this->getDefaultAction(); $data = $this->getDefaultAction();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$request->payload = $data; $request->payload = $data;
$actual = $this->actions->addAction($request, $actual = $this->actions->addAction($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemoveAction() { public function testRemoveAction() {
$this->createAutoAction(); $this->createAutoAction();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->actions->removeAction($request, $actual = $this->actions->removeAction($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Automatic action removed.', $this->assertEquals('Automatic action removed.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemoveActionForbidden() { public function testRemoveActionForbidden() {
$this->createAutoAction(); $this->createAutoAction();
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$this->actions = new AutoActions(new ContainerMock()); $this->actions = new AutoActions(new ContainerMock());
$actual = $this->actions->removeAction($request, $actual = $this->actions->removeAction($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemoveActionUnprivileged() { public function testRemoveActionUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$this->createAutoAction(); $this->createAutoAction();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->actions->removeAction($request, $actual = $this->actions->removeAction($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemovedActionInvalid() { public function testRemovedActionInvalid() {
$args = []; $args = [];
$args['id'] = 2; // No such action $args['id'] = 2; // No such action
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->actions->removeAction($request, $actual = $this->actions->removeAction($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
private function getDefaultAction() { private function getDefaultAction() {
$data = new stdClass(); $data = new stdClass();
$data->board_id = 1; $data->board_id = 1;
$data->trigger = ActionTrigger::ADDED_TO_CATEGORY; $data->trigger = ActionTrigger::ADDED_TO_CATEGORY;
$data->source_id = 1; $data->source_id = 1;
$data->type = ActionType::CLEAR_DUE_DATE; $data->type = ActionType::CLEAR_DUE_DATE;
$data->change_to = 'null'; $data->change_to = 'null';
return $data; return $data;
} }
private function createAutoAction() { private function createAutoAction() {
$auto_action = R::dispense('autoaction'); $auto_action = R::dispense('autoaction');
$auto_action->trigger = ActionTrigger::ADDED_TO_CATEGORY; $auto_action->trigger = ActionTrigger::ADDED_TO_CATEGORY;
$auto_action->source_id = 1; $auto_action->source_id = 1;
$auto_action->type = ActionType::CLEAR_DUE_DATE; $auto_action->type = ActionType::CLEAR_DUE_DATE;
$auto_action->change_to = 'null'; $auto_action->change_to = 'null';
$board = R::dispense('board'); $board = R::dispense('board');
$board->xownAutoActionList[] = $auto_action; $board->xownAutoActionList[] = $auto_action;
R::store($board); R::store($board);
} }
} }

View File

@ -3,370 +3,373 @@ require_once __DIR__ . '/../Mocks.php';
use RedBeanPHP\R; use RedBeanPHP\R;
class BoardsTest extends PHPUnit\Framework\TestCase { class BoardsTest extends PHPUnit\Framework\TestCase {
private $boards; private $boards;
public static function setupBeforeClass() { public static function setUpBeforeClass(): void {
try { try {
R::setup('sqlite:tests.db'); R::setup('sqlite:tests.db');
} catch (Exception $ex) { } catch (Exception $ex) {
}
} }
}
public function setUp() { public function setUp(): void {
R::nuke(); R::nuke();
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$this->boards = new Boards(new ContainerMock()); $this->boards = new Boards(new ContainerMock());
} }
public function testGetAllBoards() { public function testGetAllBoards() {
$this->createBoard(); $this->createBoard();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$boards = $this->boards->getAllBoards($request, $boards = $this->boards->getAllBoards($request,
new ResponseMock, null); new ResponseMock, null);
$this->assertEquals(2, count($boards->data)); $this->assertEquals(2, count($boards->data));
$this->assertEquals('success', $boards->status); $this->assertEquals('success', $boards->status);
} }
public function testGetAllBoardsNotFound() { public function testGetAllBoardsNotFound() {
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->boards->getAllBoards($request, $actual = $this->boards->getAllBoards($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('No boards in database.', $this->assertEquals('No boards in database.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetAllBoardsUnprivileged() { public function testGetAllBoardsUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->boards->getAllBoards($request, $actual = $this->boards->getAllBoards($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetBoard() { public function testGetBoard() {
$this->createBoard(); $this->createBoard();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->boards->getBoard($request, $actual = $this->boards->getBoard($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$this->assertEquals(2, count($actual->data)); $this->assertEquals(2, count($actual->data));
} }
public function testGetBoardUnprivileged() { public function testGetBoardUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->boards->getBoard($request, $actual = $this->boards->getBoard($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetBoardNotFound() { public function testGetBoardNotFound() {
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->boards->getBoard($request, $actual = $this->boards->getBoard($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('No board found for ID 1.', $this->assertEquals('No board found for ID 1.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetBoardForbidden() { public function testGetBoardForbidden() {
$this->createBoard(); $this->createBoard();
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->boards->getBoard($request, $actual = $this->boards->getBoard($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddBoard() { public function testAddBoard() {
$data = $this->getBoardData(); $data = $this->getBoardData();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $data; $request->payload = $data;
$actual = $this->boards->addBoard($request, $actual = $this->boards->addBoard($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Board test added.', $this->assertEquals('Board test added.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddBoardUnprivileged() { public function testAddBoardUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->boards->addBoard($request, $actual = $this->boards->addBoard($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddBoardInvalid() { public function testAddBoardInvalid() {
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->invalidPayload = true; $request->invalidPayload = true;
$response = $this->boards->addBoard($request, $response = $this->boards->addBoard($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
$this->assertEquals('error', $response->alerts[0]['type']); $this->assertEquals('error', $response->alerts[0]['type']);
} }
public function testUpdateBoard() { public function testUpdateBoard() {
$board = $this->getBoardUpdateData(); $board = $this->getBoardUpdateData();
$args = []; $args = [];
$args['id'] = $board->id; $args['id'] = $board->id;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $board; $request->payload = $board;
$response = $this->boards->updateBoard($request, $response = $this->boards->updateBoard($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $response->status); $this->assertEquals('success', $response->status);
} }
public function testUpdateBoardUnprivileged() { public function testUpdateBoardUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->boards->updateBoard($request, $actual = $this->boards->updateBoard($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testUpdateBoardInvalid() { public function testUpdateBoardInvalid() {
$this->createBoard(); $this->createBoard();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->invalidPayload = true; $request->invalidPayload = true;
$response = $this->boards->updateBoard($request, $response = $this->boards->updateBoard($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('error', $response->alerts[0]['type']); $this->assertEquals('error', $response->alerts[0]['type']);
} }
public function testUpdateBoardColumn() { public function testUpdateBoardColumn() {
$board = $this->getBoardUpdateData(); $board = $this->getBoardUpdateData();
$board->columns[0]->name = 'changed'; $board->columns[0]->name = 'changed';
$args = []; $args = [];
$args['id'] = $board->id; $args['id'] = $board->id;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $board; $request->payload = $board;
$response = $this->boards->updateBoard($request, $response = $this->boards->updateBoard($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$cols = $response->data[1][0]['ownColumn']; $cols = $response->data[1][0]['ownColumn'];
$this->assertEquals('success', $response->status); $this->assertEquals('success', $response->status);
$this->assertEquals('changed', $cols[0]['name']); $this->assertEquals('changed', $cols[0]['name']);
} }
public function testUpdateBoardNotFound() { public function testUpdateBoardNotFound() {
$this->createBoard(); $this->createBoard();
$board = $this->getBoardData(); $board = $this->getBoardData();
$board->id = 3; $board->id = 3;
unset($board->columns[0]->board_id); unset($board->columns[0]->board_id);
unset($board->categories[0]->board_id); unset($board->categories[0]->board_id);
unset($board->issue_trackers[0]->board_id); unset($board->issue_trackers[0]->board_id);
$args = []; $args = [];
$args['id'] = $board->id; $args['id'] = $board->id;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $board; $request->payload = $board;
$response = $this->boards->updateBoard($request, $response = $this->boards->updateBoard($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('error', $response->alerts[0]['type']); $this->assertEquals('error', $response->alerts[0]['type']);
} }
public function testUpdateBoardForbidden() { public function testUpdateBoardForbidden() {
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$request = new RequestMock();
$request->header = [DataMock::GetJwt(2)];
$actual = $this->boards->updateBoard($request,
new ResponseMock(), null);
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
}
public function testRemoveBoard() { $args = [];
$this->createBoard(); $args['id'] = 1;
$args = []; $request = new RequestMock();
$args['id'] = 1; $request->header = [DataMock::GetJwt(2)];
$action = R::dispense('autoaction');
$action->board_id = 1;
R::store($action);
$request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$actual = $this->boards->removeBoard($request,
new ResponseMock(), $args);
$this->assertEquals('Board test removed.',
$actual->alerts[0]['text']);
}
public function testRemoveBoardUnprivileged() { $actual = $this->boards->updateBoard($request,
DataMock::CreateUnprivilegedUser(); new ResponseMock(), $args);
$this->assertEquals('Access restricted.',
$request = new RequestMock(); $actual->alerts[0]['text']);
$request->header = [DataMock::GetJwt(2)]; }
$actual = $this->boards->removeBoard($request,
new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
public function testRemoveBoardInvalid() {
$request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$args = [];
$args['id'] = 1; // No such board
$this->boards = new Boards(new ContainerMock());
$response = $this->boards->removeBoard($request,
new ResponseMock(), $args);
$this->assertEquals('failure', $response->status);
}
private function getBoardUpdateData() {
$this->createBoard();
$existing = R::load('board', 1);
$column = R::dispense('column');
$column->name = 'one';
$column->position = 1;
$existing->xownColumnList[] = $column;
$column = R::dispense('column');
$column->name = 'two';
$column->position = 2;
$existing->xownColumnList[] = $column;
$category = R::dispense('category');
$existing->xownCategoryList[] = $category;
R::store($existing);
$user = R::dispense('user');
$user->username = 'test';
R::store($user);
$board = $this->getBoardData();
$board->id = 1;
$newColumn = new stdClass();
$newColumn->name = 'col1';
$newColumn->position = 0;
$board->columns[] = $newColumn;
$board->users[] = $user->export();
$board->issue_trackers[0]->board_id = 1;
$board->categories = [];
return $board;
}
private function getBoardData() {
$board = new stdClass();
$tracker = new stdClass();
$category = new stdClass();
$column = new stdClass();
$column->name = 'col2';
$column->position = 1;
$column->id = 1;
$column->board_id = 1;
$category->name = 'cat 1';
$category->default_task_color = '';
$category->board_id = 1;
$tracker->url = 'testing';
$tracker->regex = '';
$tracker->board_id = 1;
$board->name = 'test'; public function testRemoveBoard() {
$board->is_active = true; $this->createBoard();
$board->sharedUserList[] = R::load('user', 1);
$args = [];
$board->issue_trackers[] = $tracker; $args['id'] = 1;
$board->categories[] = $category;
$board->columns[] = $column; $action = R::dispense('autoaction');
$board->users = []; $action->board_id = 1;
R::store($action);
return $board;
} $request = new RequestMock();
$request->header = [DataMock::GetJwt()];
private function createBoard() {
$board = R::dispense('board'); $actual = $this->boards->removeBoard($request,
$column = R::dispense('column'); new ResponseMock(), $args);
$task = R::dispense('task'); $this->assertEquals('Board test removed.',
$actual->alerts[0]['text']);
$task->sharedUserList[] = R::load('user', 1); }
$column->name = 'test'; public function testRemoveBoardUnprivileged() {
$column->position = 0; DataMock::CreateUnprivilegedUser();
$column->xownTaskList[] = $task;
$request = new RequestMock();
$board->name = 'test'; $request->header = [DataMock::GetJwt(2)];
$board->is_active = true;
$board->sharedUserList[] = R::load('user', 1); $actual = $this->boards->removeBoard($request,
$board->xownColumnList[] = $column; new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
R::store($board); $actual->alerts[0]['text']);
} }
public function testRemoveBoardInvalid() {
$request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$args = [];
$args['id'] = 1; // No such board
$this->boards = new Boards(new ContainerMock());
$response = $this->boards->removeBoard($request,
new ResponseMock(), $args);
$this->assertEquals('failure', $response->status);
}
private function getBoardUpdateData() {
$this->createBoard();
$existing = R::load('board', 1);
$column = R::dispense('column');
$column->name = 'one';
$column->position = 1;
$existing->xownColumnList[] = $column;
$column = R::dispense('column');
$column->name = 'two';
$column->position = 2;
$existing->xownColumnList[] = $column;
$category = R::dispense('category');
$existing->xownCategoryList[] = $category;
R::store($existing);
$user = R::dispense('user');
$user->username = 'test';
R::store($user);
$board = $this->getBoardData();
$board->id = 1;
$newColumn = new stdClass();
$newColumn->name = 'col1';
$newColumn->position = 0;
$board->columns[] = $newColumn;
$board->users[] = $user->export();
$board->issue_trackers[0]->board_id = 1;
$board->categories = [];
return $board;
}
private function getBoardData() {
$board = new stdClass();
$tracker = new stdClass();
$category = new stdClass();
$column = new stdClass();
$column->name = 'col2';
$column->position = 1;
$column->id = 1;
$column->board_id = 1;
$category->name = 'cat 1';
$category->default_task_color = '';
$category->board_id = 1;
$tracker->url = 'testing';
$tracker->regex = '';
$tracker->board_id = 1;
$board->name = 'test';
$board->is_active = true;
$board->sharedUserList[] = R::load('user', 1);
$board->issue_trackers[] = $tracker;
$board->categories[] = $category;
$board->columns[] = $column;
$board->users = [];
return $board;
}
private function createBoard() {
$board = R::dispense('board');
$column = R::dispense('column');
$task = R::dispense('task');
$task->sharedUserList[] = R::load('user', 1);
$column->name = 'test';
$column->position = 0;
$column->xownTaskList[] = $task;
$board->name = 'test';
$board->is_active = true;
$board->sharedUserList[] = R::load('user', 1);
$board->xownColumnList[] = $column;
R::store($board);
}
} }

View File

@ -3,272 +3,279 @@ require_once __DIR__ . '/../Mocks.php';
use RedBeanPHP\R; use RedBeanPHP\R;
class ColumnsTest extends PHPUnit\Framework\TestCase { class ColumnsTest extends PHPUnit\Framework\TestCase {
private $columns; private $columns;
public static function setupBeforeClass() { public static function setUpBeforeClass(): void {
try { try {
R::setup('sqlite:tests.db'); R::setup('sqlite:tests.db');
} catch (Exception $ex) { } catch (Exception $ex) {
}
} }
}
public function setUp() { public function setUp(): void {
R::nuke(); R::nuke();
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$this->columns = new Columns(new ContainerMock()); $this->columns = new Columns(new ContainerMock());
} }
public function testGetColumn() { public function testGetColumn() {
$this->createColumn(); $this->createColumn();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$actual = $this->columns->getColumn($request, $actual = $this->columns->getColumn($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$this->assertEquals(2, count($actual->data)); $this->assertEquals(2, count($actual->data));
} }
public function testGetColumnNotFound() { public function testGetColumnNotFound() {
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$actual = $this->columns->getColumn($request, $actual = $this->columns->getColumn($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
public function testGetColumnUnprivileged() { public function testGetColumnUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->columns->getColumn($request, $actual = $this->columns->getColumn($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetColumnForbidden() { public function testGetColumnForbidden() {
$this->createColumn(); $this->createColumn();
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->columns->getColumn($request, $actual = $this->columns->getColumn($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddColumn() { public function testAddColumn() {
$board = R::dispense('board'); $board = R::dispense('board');
R::store($board); R::store($board);
$this->createColumn(); $this->createColumn();
$data = $this->getColumnData(); $data = $this->getColumnData();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $data; $request->payload = $data;
$actual = $this->columns->addColumn($request, $actual = $this->columns->addColumn($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
} }
public function testAddColumnUnprivileged() { public function testAddColumnUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->columns->addColumn($request, $actual = $this->columns->addColumn($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddColumnInvalid() { public function testAddColumnInvalid() {
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->invalidPayload = true; $request->invalidPayload = true;
$response = $this->columns->addColumn($request, $response = $this->columns->addColumn($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
$this->assertEquals('error', $response->alerts[0]['type']); $this->assertEquals('error', $response->alerts[0]['type']);
} }
public function testAddColumnForbidden() { public function testAddColumnForbidden() {
$this->createColumn(); $this->createColumn();
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$column = $this->getColumnData(); $column = $this->getColumnData();
$column->id = 0; $column->id = 0;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$request->payload = $column; $request->payload = $column;
$actual = $this->columns->addColumn($request, $actual = $this->columns->addColumn($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testUpdateColumn() { public function testUpdateColumn() {
$this->createColumn(); $this->createColumn();
$column = $this->getColumnData(); $column = $this->getColumnData();
$column->id = 1; $column->id = 1;
$column->name = 'updated'; $column->name = 'updated';
$args = []; $args = [];
$args['id'] = $column->id; $args['id'] = $column->id;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $column; $request->payload = $column;
$response = $this->columns->updateColumn($request, $response = $this->columns->updateColumn($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $response->status); $this->assertEquals('success', $response->status);
} }
public function testUpdateColumnUnprivileged() { public function testUpdateColumnUnprivileged() {
$this->createColumn(); $this->createColumn();
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->columns->updateColumn($request, $actual = $this->columns->updateColumn($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testUpdateColumnForbidden() { public function testUpdateColumnForbidden() {
$this->createColumn(); $this->createColumn();
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$column = $this->getColumnData(); $column = $this->getColumnData();
$column->id = 1; $column->id = 1;
$column->name = 'test'; $column->name = 'test';
$args = []; $args = [];
$args['id'] = $column->id; $args['id'] = $column->id;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$request->payload = $column; $request->payload = $column;
$this->columns = new Columns(new ContainerMock()); $this->columns = new Columns(new ContainerMock());
$actual = $this->columns->updateColumn($request, $actual = $this->columns->updateColumn($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testUpdateColumnInvalid() { public function testUpdateColumnInvalid() {
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->invalidPayload = true; $request->invalidPayload = true;
$response = $this->columns->updateColumn($request, $args = [];
new ResponseMock(), null); $args['id'] = 1;
$this->assertEquals('error', $response->alerts[0]['type']);
}
public function testRemoveColumn() { $response = $this->columns->updateColumn($request,
$this->createColumn(); new ResponseMock(), $args);
$this->assertEquals('error', $response->alerts[0]['type']);
$args = []; $response = $this->columns->updateColumn($request,
$args['id'] = 1; new ResponseMock(), null);
$this->assertEquals('error', $response->alerts[0]['type']);
}
$request = new RequestMock(); public function testRemoveColumn() {
$request->header = [DataMock::GetJwt()]; $this->createColumn();
$actual = $this->columns->removeColumn($request, $args = [];
new ResponseMock(), $args); $args['id'] = 1;
$this->assertEquals('success', $actual->status);
}
public function testRemoveColumnUnprivileged() { $request = new RequestMock();
DataMock::CreateUnprivilegedUser(); $request->header = [DataMock::GetJwt()];
$request = new RequestMock(); $actual = $this->columns->removeColumn($request,
$request->header = [DataMock::GetJwt(2)]; new ResponseMock(), $args);
$this->assertEquals('success', $actual->status);
}
$actual = $this->columns->removeColumn($request, public function testRemoveColumnUnprivileged() {
new ResponseMock(), null); DataMock::CreateUnprivilegedUser();
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
public function testRemoveColumnInvalid() { $request = new RequestMock();
$args = []; $request->header = [DataMock::GetJwt(2)];
$args['id'] = 1; // No such column
$request = new RequestMock(); $actual = $this->columns->removeColumn($request,
$request->header = [DataMock::GetJwt()]; new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
$response = $this->columns->removeColumn($request, public function testRemoveColumnInvalid() {
new ResponseMock(), $args); $args = [];
$this->assertEquals('failure', $response->status); $args['id'] = 1; // No such column
}
public function testRemoveColumnForbidden() { $request = new RequestMock();
$this->createColumn(); $request->header = [DataMock::GetJwt()];
DataMock::CreateBoardAdminUser();
$args = []; $response = $this->columns->removeColumn($request,
$args['id'] = 1; new ResponseMock(), $args);
$this->assertEquals('failure', $response->status);
}
$request = new RequestMock(); public function testRemoveColumnForbidden() {
$request->header = [DataMock::GetJwt(2)]; $this->createColumn();
DataMock::CreateBoardAdminUser();
$actual = $this->columns->removeColumn($request, $args = [];
new ResponseMock(), $args); $args['id'] = 1;
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
}
private function getColumnData() { $request = new RequestMock();
$data = new stdClass(); $request->header = [DataMock::GetJwt(2)];
$data->name = 'test'; $actual = $this->columns->removeColumn($request,
$data->position = 0; new ResponseMock(), $args);
$data->board_id = 1; $this->assertEquals('Access restricted.',
$data->tasks = []; $actual->alerts[0]['text']);
}
return $data; private function getColumnData() {
} $data = new stdClass();
private function createColumn() { $data->name = 'test';
$column = R::dispense('column'); $data->position = 0;
$data->board_id = 1;
$data->tasks = [];
$board = R::dispense('board'); return $data;
$board->xownColumnList[] = $column; }
R::store($board); private function createColumn() {
} $column = R::dispense('column');
$board = R::dispense('board');
$board->xownColumnList[] = $column;
R::store($board);
}
} }

View File

@ -3,313 +3,320 @@ require_once __DIR__ . '/../Mocks.php';
use RedBeanPHP\R; use RedBeanPHP\R;
class CommentsTest extends PHPUnit\Framework\TestCase { class CommentsTest extends PHPUnit\Framework\TestCase {
private $comments; private $comments;
public static function setupBeforeClass() { public static function setUpBeforeClass(): void {
try { try {
R::setup('sqlite:tests.db'); R::setup('sqlite:tests.db');
} catch (Exception $ex) { } catch (Exception $ex) {
}
} }
}
public function setUp() { public function setUp(): void {
R::nuke(); R::nuke();
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$this->comments = new Comments(new ContainerMock()); $this->comments = new Comments(new ContainerMock());
} }
public function testGetComment() { public function testGetComment() {
$this->createComment(); $this->createComment();
$request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$request = new RequestMock(); $args = [];
$request->header = [DataMock::GetJwt()]; $args['id'] = 1;
$actual = $this->comments->getComment($request,
new ResponseMock(), $args);
$this->assertEquals('success', $actual->status);
$this->assertEquals(2, count($actual->data));
}
public function testGetCommentNotFound() {
$request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$args = [];
$args['id'] = 1;
$args = []; $actual = $this->comments->getComment($request,
$args['id'] = 1; new ResponseMock(), $args);
$this->assertEquals('No comment found for ID 1.',
$actual = $this->comments->getComment($request, $actual->alerts[0]['text']);
new ResponseMock(), $args); }
$this->assertEquals('success', $actual->status);
$this->assertEquals(2, count($actual->data)); public function testGetCommentForbidden() {
} $this->createComment();
DataMock::CreateBoardAdminUser();
$args = [];
$args['id'] = 1;
$request = new RequestMock();
$request->header = [DataMock::GetJwt(2)];
public function testGetCommentNotFound() { $this->comments = new Comments(new ContainerMock());
$request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $actual = $this->comments->getComment($request,
new ResponseMock(), $args);
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
}
public function testGetCommentUnprivileged() {
DataMock::CreateUnprivilegedUser();
$request = new RequestMock();
$request->header = [DataMock::GetJwt(2)];
$actual = $this->comments->getComment($request,
new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
public function testAddComment() {
$this->createComment();
$data = $this->getCommentData();
$request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$request->payload = $data;
$actual = $this->comments->addComment($request,
new ResponseMock(), null);
$this->assertEquals('success', $actual->status);
}
public function testAddCommentUnprivileged() {
DataMock::CreateUnprivilegedUser();
$comment = $this->getCommentData();
$request = new RequestMock();
$request->header = [DataMock::GetJwt(2)];
$request->payload = $comment;
$actual = $this->comments->addComment($request,
new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
public function testAddCommentInvalid() {
$request = new RequestMock();
$request->invalidPayload = true;
$request->header = [DataMock::GetJwt()];
$args = []; $actual = $this->comments->addComment($request,
$args['id'] = 1; new ResponseMock(), null);
$this->assertEquals('failure', $actual->status);
$this->assertEquals('error', $actual->alerts[0]['type']);
}
public function testAddCommentForbidden() {
$this->createComment();
DataMock::createBoardAdminUser();
$comment = $this->getCommentData();
$request = new RequestMock();
$request->header = [DataMock::GetJwt(2)];
$request->payload = $comment;
$actual = $this->comments->addComment($request,
new ResponseMock(), null);
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
}
public function testUpdateComment() {
$this->createComment();
$actual = $this->comments->getComment($request, $comment = $this->getCommentData();
new ResponseMock(), $args); $comment->id = 1;
$this->assertEquals('No comment found for ID 1.', $comment->text = 'updated';
$actual->alerts[0]['text']);
}
public function testGetCommentForbidden() {
$this->createComment();
DataMock::CreateBoardAdminUser();
$args = [];
$args['id'] = 1;
$request = new RequestMock(); $args = [];
$request->header = [DataMock::GetJwt(2)]; $args['id'] = $comment->id;
$this->comments = new Comments(new ContainerMock()); $request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$actual = $this->comments->getComment($request, $request->payload = $comment;
new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $response = $this->comments->updateComment($request,
$actual->alerts[0]['text']); new ResponseMock(), $args);
} $this->assertEquals('success', $response->status);
}
public function testGetCommentUnprivileged() { public function testUpdateCommentInvalid() {
DataMock::CreateUnprivilegedUser(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$request = new RequestMock(); $request->invalidPayload = true;
$request->header = [DataMock::GetJwt(2)];
$actual = $this->comments->getComment($request,
new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
public function testAddComment() {
$this->createComment();
$data = $this->getCommentData();
$request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$request->payload = $data;
$actual = $this->comments->addComment($request,
new ResponseMock(), null);
$this->assertEquals('success', $actual->status);
}
public function testAddCommentUnprivileged() {
DataMock::CreateUnprivilegedUser();
$comment = $this->getCommentData();
$request = new RequestMock();
$request->header = [DataMock::GetJwt(2)];
$request->payload = $comment;
$actual = $this->comments->addComment($request,
new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
public function testAddCommentInvalid() {
$request = new RequestMock();
$request->invalidPayload = true;
$request->header = [DataMock::GetJwt()];
$actual = $this->comments->addComment($request, $args = [];
new ResponseMock(), null); $args['id'] = 1;
$this->assertEquals('failure', $actual->status);
$this->assertEquals('error', $actual->alerts[0]['type']); $response = $this->comments->updateComment($request,
} new ResponseMock(), $args);
$this->assertEquals('error', $response->alerts[0]['type']);
$response = $this->comments->updateComment($request,
new ResponseMock(), null);
$this->assertEquals('error', $response->alerts[0]['type']);
}
public function testUpdateCommentUnprivileged() {
DataMock::CreateUnprivilegedUser();
$request = new RequestMock();
$request->header = [DataMock::GetJwt(2)];
$actual = $this->comments->updateComment($request,
new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
public function testUpdateCommentForbidden() {
$this->createComment();
DataMock::createBoardAdminUser();
public function testAddCommentForbidden() { $comment = $this->getCommentData();
$this->createComment(); $comment->id = 1;
DataMock::createBoardAdminUser(); $comment->text = 'updated';
$comment = $this->getCommentData();
$request = new RequestMock();
$request->header = [DataMock::GetJwt(2)];
$request->payload = $comment;
$actual = $this->comments->addComment($request,
new ResponseMock(), null);
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
}
public function testUpdateComment() {
$this->createComment();
$comment = $this->getCommentData();
$comment->id = 1;
$comment->text = 'updated';
$args = [];
$args['id'] = $comment->id;
$request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$request->payload = $comment;
$response = $this->comments->updateComment($request,
new ResponseMock(), $args);
$this->assertEquals('success', $response->status);
}
public function testUpdateCommentInvalid() {
$request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$request->invalidPayload = true;
$response = $this->comments->updateComment($request, $args = [];
new ResponseMock(), null); $args['id'] = $comment->id;
$this->assertEquals('error', $response->alerts[0]['type']);
}
public function testUpdateCommentUnprivileged() {
DataMock::CreateUnprivilegedUser();
$request = new RequestMock();
$request->header = [DataMock::GetJwt(2)];
$actual = $this->comments->updateComment($request,
new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
public function testUpdateCommentForbidden() {
$this->createComment();
DataMock::createBoardAdminUser();
$comment = $this->getCommentData(); $request = new RequestMock();
$comment->id = 1; $request->header = [DataMock::GetJwt(2)];
$comment->text = 'updated'; $request->payload = $comment;
$args = []; $actual = $this->comments->updateComment($request,
$args['id'] = $comment->id; new ResponseMock(), $args);
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
}
$request = new RequestMock(); public function testUpdateCommentUserSecurity() {
$request->header = [DataMock::GetJwt(2)]; $this->createComment();
$request->payload = $comment; DataMock::CreateStandardUser();
$actual = $this->comments->updateComment($request, $args = [];
new ResponseMock(), $args); $args['id'] = 1;
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
}
public function testUpdateCommentUserSecurity() { $request = new RequestMock();
$this->createComment(); $request->header = [DataMock::GetJwt(2)];
DataMock::CreateStandardUser();
$args = []; $actual = $this->comments->updateComment($request,
$args['id'] = 1; new ResponseMock(), $args);
$request = new RequestMock(); $this->assertEquals('You do not have sufficient permissions to ' .
$request->header = [DataMock::GetJwt(2)]; 'update this comment.', $actual->alerts[0]['text']);
}
$actual = $this->comments->updateComment($request, public function testRemoveComment() {
new ResponseMock(), $args); $this->createComment();
$this->assertEquals('You do not have sufficient permissions to ' . $args = [];
'update this comment.', $actual->alerts[0]['text']); $args['id'] = 1;
}
public function testRemoveComment() { $request = new RequestMock();
$this->createComment(); $request->header = [DataMock::GetJwt()];
$args = []; $actual = $this->comments->removeComment($request,
$args['id'] = 1; new ResponseMock(), $args);
$this->assertEquals('success', $actual->status);
}
$request = new RequestMock(); public function testRemoveCommentUnprivileged() {
$request->header = [DataMock::GetJwt()]; DataMock::CreateUnprivilegedUser();
$actual = $this->comments->removeComment($request, $request = new RequestMock();
new ResponseMock(), $args); $request->header = [DataMock::GetJwt(2)];
$this->assertEquals('success', $actual->status);
}
public function testRemoveCommentUnprivileged() { $actual = $this->comments->removeComment($request,
DataMock::CreateUnprivilegedUser(); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
$request = new RequestMock(); public function testRemoveCommentInvalid() {
$request->header = [DataMock::GetJwt(2)]; $request = new RequestMock();
$request->header = [DataMock::GetJwt()];
$actual = $this->comments->removeComment($request, $args = [];
new ResponseMock(), null); $args['id'] = 1; // No such comment
$this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']);
}
public function testRemoveCommentInvalid() { $response = $this->comments->removeComment($request,
$request = new RequestMock(); new ResponseMock(), $args);
$request->header = [DataMock::GetJwt()]; $this->assertEquals('failure', $response->status);
}
$args = []; public function testRemoveCommentForbidden() {
$args['id'] = 1; // No such comment $this->createComment();
DataMock::CreateBoardAdminUser();
$response = $this->comments->removeComment($request, $args = [];
new ResponseMock(), $args); $args['id'] = 1;
$this->assertEquals('failure', $response->status);
}
public function testRemoveCommentForbidden() { $request = new RequestMock();
$this->createComment(); $request->header = [DataMock::GetJwt(2)];
DataMock::CreateBoardAdminUser();
$args = []; $actual = $this->comments->removeComment($request,
$args['id'] = 1; new ResponseMock(), $args);
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
}
$request = new RequestMock(); public function testRemoveCommentUserSecurity() {
$request->header = [DataMock::GetJwt(2)]; $this->createComment();
DataMock::CreateStandardUser();
$actual = $this->comments->removeComment($request, $args = [];
new ResponseMock(), $args); $args['id'] = 1;
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
}
public function testRemoveCommentUserSecurity() { $this->comments = new Comments(new ContainerMock());
$this->createComment();
DataMock::CreateStandardUser();
$args = []; $request = new RequestMock();
$args['id'] = 1; $request->header = [DataMock::GetJwt(2)];
$this->comments = new Comments(new ContainerMock()); $actual = $this->comments->removeComment($request,
new ResponseMock(), $args);
$request = new RequestMock(); $this->assertEquals('You do not have sufficient permissions to ' .
$request->header = [DataMock::GetJwt(2)]; 'remove this comment.', $actual->alerts[0]['text']);
}
$actual = $this->comments->removeComment($request,
new ResponseMock(), $args);
$this->assertEquals('You do not have sufficient permissions to ' . private function getCommentData() {
'remove this comment.', $actual->alerts[0]['text']); $data = new stdClass();
}
$data->text = 'test comment';
$data->user_id = 1;
$data->task_id = 1;
$data->timestamp = time();
private function getCommentData() { return $data;
$data = new stdClass(); }
$data->text = 'test comment'; private function createComment() {
$data->user_id = 1; $comment = R::dispense('comment');
$data->task_id = 1; R::store($comment);
$data->timestamp = time();
return $data; $task = R::dispense('task');
} $task->xownCommentList[] = $comment;
private function createComment() { $column = R::dispense('column');
$comment = R::dispense('comment'); $column->xownTaskList[] = $task;
R::store($comment);
$task = R::dispense('task'); $admin = R::load('user', 1);
$task->xownCommentList[] = $comment; $board = R::dispense('board');
$board->xownColumnList[] = $column;
$board->sharedUserList[] = $admin;
$column = R::dispense('column'); R::store($board);
$column->xownTaskList[] = $task; }
$admin = R::load('user', 1);
$board = R::dispense('board');
$board->xownColumnList[] = $column;
$board->sharedUserList[] = $admin;
R::store($board);
}
} }

View File

@ -3,436 +3,443 @@ require_once __DIR__ . '/../Mocks.php';
use RedBeanPHP\R; use RedBeanPHP\R;
class TasksTest extends PHPUnit\Framework\TestCase { class TasksTest extends PHPUnit\Framework\TestCase {
private $tasks; private $tasks;
public static function setupBeforeClass() { public static function setUpBeforeClass(): void {
try { try {
R::setup('sqlite:tests.db'); R::setup('sqlite:tests.db');
} catch (Exception $ex) { } catch (Exception $ex) {
}
} }
}
public function setUp() { public function setUp(): void {
R::nuke(); R::nuke();
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$this->tasks = new Tasks(new ContainerMock()); $this->tasks = new Tasks(new ContainerMock());
} }
public function testGetTask() { public function testGetTask() {
$this->createTask(); $this->createTask();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::getJwt()]; $request->header = [DataMock::getJwt()];
$actual = $this->tasks->getTask($request, new ResponseMock(), $args); $actual = $this->tasks->getTask($request, new ResponseMock(), $args);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$this->assertEquals(2, count($actual->data)); $this->assertEquals(2, count($actual->data));
} }
public function testGetTaskNotFound() { public function testGetTaskNotFound() {
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::getJwt()]; $request->header = [DataMock::getJwt()];
$actual = $this->tasks->getTask($request, new ResponseMock(), $args); $actual = $this->tasks->getTask($request, new ResponseMock(), $args);
$this->assertEquals('No task found for ID 1.', $this->assertEquals('No task found for ID 1.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetTaskForbidden() { public function testGetTaskForbidden() {
$this->createTask(); $this->createTask();
DataMock::CreateBoardAdminUser(); DataMock::CreateBoardAdminUser();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::getJwt(2)]; $request->header = [DataMock::getJwt(2)];
$actual = $this->tasks->getTask($request, new ResponseMock(), $args); $actual = $this->tasks->getTask($request, new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetTaskUnprivileged() { public function testGetTaskUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::getJwt(2)]; $request->header = [DataMock::getJwt(2)];
$actual = $this->tasks->getTask($request, new ResponseMock(), null); $actual = $this->tasks->getTask($request, new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddTask() { public function testAddTask() {
$this->createTask(); $this->createTask();
$data = $this->getTaskData(); $data = $this->getTaskData();
$assignee = R::load('user', 1); $assignee = R::load('user', 1);
$data->assignees[] = $assignee; $data->assignees[] = $assignee;
$category = R::load('category', 1); $category = R::load('category', 1);
$category->name = 'Front End'; $category->name = 'Front End';
R::store($category); R::store($category);
$data->categories[] = $category; $data->categories[] = $category;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $data; $request->payload = $data;
$actual = $this->tasks->addTask($request, new ResponseMock(), null); $actual = $this->tasks->addTask($request, new ResponseMock(), null);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
} }
public function testAddTaskTop() { public function testAddTaskTop() {
$this->createTask(); $this->createTask();
$data = $this->getTaskData(); $data = $this->getTaskData();
$user = R::load('user', 1); $user = R::load('user', 1);
$opts = R::load('useroption', $user->user_option_id); $opts = R::load('useroption', $user->user_option_id);
$opts->new_tasks_at_bottom = false; $opts->new_tasks_at_bottom = false;
R::store($opts); R::store($opts);
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $data; $request->payload = $data;
$actual = $this->tasks->addTask($request, new ResponseMock(), null); $actual = $this->tasks->addTask($request, new ResponseMock(), null);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
} }
public function testAddTaskUnprivileged() { public function testAddTaskUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request= new RequestMock(); $request= new RequestMock();
$request->header = [DataMock::getJwt(2)]; $request->header = [DataMock::getJwt(2)];
$actual = $this->tasks->addTask($request, new ResponseMock(), null); $actual = $this->tasks->addTask($request, new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddTaskInvalid() { public function testAddTaskInvalid() {
$request = new RequestMock(); $request = new RequestMock();
$request->invalidPayload = true; $request->invalidPayload = true;
$request->header = [DataMock::getJwt()]; $request->header = [DataMock::getJwt()];
$response = $this->tasks->addTask($request,
new ResponseMock(), null);
$this->assertEquals('failure', $response->status);
$this->assertEquals('error', $response->alerts[0]['type']);
}
public function testAddTaskForbidden() { $response = $this->tasks->addTask($request,
$this->createTask(); new ResponseMock(), null);
DataMock::CreateBoardAdminUser();
$this->assertEquals('failure', $response->status);
$this->assertEquals('error', $response->alerts[0]['type']);
}
$task = $this->getTaskData(); public function testAddTaskForbidden() {
$task->id = 0; $this->createTask();
DataMock::CreateBoardAdminUser();
$request = new RequestMock(); $task = $this->getTaskData();
$request->header = [DataMock::getJwt(2)]; $task->id = 0;
$request->payload = $task;
$actual = $this->tasks->addTask($request, new ResponseMock(), null); $request = new RequestMock();
$this->assertEquals('Access restricted.', $request->header = [DataMock::getJwt(2)];
$actual->alerts[0]['text']); $request->payload = $task;
}
public function testUpdateTask() {
$this->createTask();
$task = $this->getTaskData();
$task->id = 1;
$task->title = 'updated';
$args = [];
$args['id'] = $task->id;
$request = new RequestMock();
$request->header = [DataMock::getJwt()];
$request->payload = $task;
$response = $this->tasks->updateTask($request,
new ResponseMock(), $args);
$this->assertEquals('success', $response->status);
$this->assertEquals('updated', $response->data[1][0]['title']);
}
public function testUpdateTaskWithActions() {
$this->addActions();
$this->createTask();
DataMock::CreateStandardUser();
$task = $this->getTaskData();
$task->id = 1;
$task->column_id = 2;
$task->title = 'updated';
$task->assignees = [];
$task->assignees[] = R::load('user', 2);
$task->categories = [];
$task->categories[] = R::load('category', 2);
$task->points = 5;
$task->due_date = '1/1/2017';
$args = []; $actual = $this->tasks->addTask($request, new ResponseMock(), null);
$args['id'] = $task->id; $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']);
$request = new RequestMock(); }
$request->header = [DataMock::getJwt()];
$request->payload = $task; public function testUpdateTask() {
$this->createTask();
$response = $this->tasks->updateTask($request,
new ResponseMock(), $args); $task = $this->getTaskData();
$this->assertEquals('success', $response->status); $task->id = 1;
$task->title = 'updated';
$updated = $response->data[1][0];
$this->assertEquals('updated', $updated['title']); $args = [];
$this->assertEquals('#cdcdcd', $updated['color']); $args['id'] = $task->id;
$this->assertEquals(2, (int)$updated['sharedUser'][0]['id']);
$this->assertEquals('', $updated['due_date']); $request = new RequestMock();
$request->header = [DataMock::getJwt()];
$temp = R::load('task', 1); $request->payload = $task;
$temp->due_date = '';
R::store($temp); $response = $this->tasks->updateTask($request,
new ResponseMock(), $args);
$task->column_id = 1; $this->assertEquals('success', $response->status);
$task->due_date =''; $this->assertEquals('updated', $response->data[1][0]['title']);
$request = new RequestMock(); }
$request->header = [DataMock::getJwt()];
$request->payload = $task; public function testUpdateTaskWithActions() {
$this->addActions();
$this->createTask();
DataMock::CreateStandardUser();
$task = $this->getTaskData();
$task->id = 1;
$task->column_id = 2;
$task->title = 'updated';
$task->assignees = [];
$task->assignees[] = R::load('user', 2);
$task->categories = [];
$task->categories[] = R::load('category', 2);
$task->points = 5;
$task->due_date = '1/1/2017';
$response = $this->tasks->updateTask($request, $args = [];
new ResponseMock(), $args); $args['id'] = $task->id;
$this->assertEquals('success', $response->status);
} $request = new RequestMock();
$request->header = [DataMock::getJwt()];
public function testUpdateTaskInvalid() { $request->payload = $task;
$request = new RequestMock();
$request->header = [DataMock::getJwt()]; $response = $this->tasks->updateTask($request,
$request->invalidPayload = true; new ResponseMock(), $args);
$this->assertEquals('success', $response->status);
$response = $this->tasks->updateTask($request,
new ResponseMock(), null); $updated = $response->data[1][0];
$this->assertEquals('error', $response->alerts[0]['type']); $this->assertEquals('updated', $updated['title']);
} $this->assertEquals('#cdcdcd', $updated['color']);
$this->assertEquals(2, (int)$updated['sharedUser'][0]['id']);
public function testUpdateTaskUnprivileged() { $this->assertEquals('', $updated['due_date']);
DataMock::CreateUnprivilegedUser();
$temp = R::load('task', 1);
$request = new RequestMock(); $temp->due_date = '';
$request->header = [DataMock::getJwt(2)]; R::store($temp);
$actual = $this->tasks->updateTask($request, $task->column_id = 1;
new ResponseMock(), null); $task->due_date ='';
$this->assertEquals('Insufficient privileges.', $request = new RequestMock();
$actual->alerts[0]['text']); $request->header = [DataMock::getJwt()];
} $request->payload = $task;
public function testUpdateTaskForbidden() { $response = $this->tasks->updateTask($request,
$this->createTask(); new ResponseMock(), $args);
DataMock::CreateBoardAdminUser(); $this->assertEquals('success', $response->status);
}
$task = $this->getTaskData();
$task->id = 1; public function testUpdateTaskInvalid() {
$task->title = 'updated'; $request = new RequestMock();
$request->header = [DataMock::getJwt()];
$args = []; $request->invalidPayload = true;
$args['id'] = $task->id;
$args = [];
$request = new RequestMock(); $args['id'] = 1;
$request->header = [DataMock::getJwt(2)];
$request->payload = $task; $response = $this->tasks->updateTask($request,
new ResponseMock(), $args);
$actual = $this->tasks->updateTask($request, $this->assertEquals('error', $response->alerts[0]['type']);
new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $response = $this->tasks->updateTask($request,
$actual->alerts[0]['text']); new ResponseMock(), null);
} $this->assertEquals('error', $response->alerts[0]['type']);
}
public function testRemoveTask() {
$this->createTask(); public function testUpdateTaskUnprivileged() {
DataMock::CreateUnprivilegedUser();
$args = [];
$args['id'] = 1; $request = new RequestMock();
$request->header = [DataMock::getJwt(2)];
$request = new RequestMock();
$request->header = [DataMock::getJwt()]; $actual = $this->tasks->updateTask($request,
new ResponseMock(), null);
$actual = $this->tasks->removeTask($request, $this->assertEquals('Insufficient privileges.',
new ResponseMock(), $args); $actual->alerts[0]['text']);
}
$this->assertEquals('Task test removed.', $actual->alerts[0]['text']);
} public function testUpdateTaskForbidden() {
$this->createTask();
public function testRemoveTaskUnprivileged() { DataMock::CreateBoardAdminUser();
DataMock::CreateUnprivilegedUser();
$task = $this->getTaskData();
$args = []; $task->id = 1;
$args['id'] = 1; $task->title = 'updated';
$request = new RequestMock(); $args = [];
$request->header = [DataMock::getJwt(2)]; $args['id'] = $task->id;
$actual = $this->tasks->removeTask($request, $request = new RequestMock();
new ResponseMock(), $args); $request->header = [DataMock::getJwt(2)];
$this->assertEquals('Insufficient privileges.', $request->payload = $task;
$actual->alerts[0]['text']);
} $actual = $this->tasks->updateTask($request,
new ResponseMock(), $args);
public function testRemoveTaskInvalid() { $this->assertEquals('Access restricted.',
$request = new RequestMock(); $actual->alerts[0]['text']);
$request->header = [DataMock::getJwt()]; }
$args = []; public function testRemoveTask() {
$args['id'] = 2; // No such task $this->createTask();
$response = $this->tasks->removeTask($request, $args = [];
new ResponseMock(), $args); $args['id'] = 1;
$this->assertEquals('failure', $response->status);
} $request = new RequestMock();
$request->header = [DataMock::getJwt()];
public function testRemoveTaskForbidden() {
$this->createTask(); $actual = $this->tasks->removeTask($request,
DataMock::CreateBoardAdminUser(); new ResponseMock(), $args);
$request = new RequestMock(); $this->assertEquals('Task test removed.', $actual->alerts[0]['text']);
$request->header = [DataMock::getJwt(2)]; }
$args = []; public function testRemoveTaskUnprivileged() {
$args['id'] = 1; DataMock::CreateUnprivilegedUser();
$actual = $this->tasks->removeTask($request, $args = [];
new ResponseMock(), $args); $args['id'] = 1;
$this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $request = new RequestMock();
} $request->header = [DataMock::getJwt(2)];
private function addActions() { $actual = $this->tasks->removeTask($request,
$action = R::dispense('autoaction'); new ResponseMock(), $args);
$action->board_id = 1; $this->assertEquals('Insufficient privileges.',
$action->trigger = 1; // Moved to column $actual->alerts[0]['text']);
$action->source_id = 2; // Column ID }
$action->type = 1; // Set color
$action->change_to = '#fff'; public function testRemoveTaskInvalid() {
$request = new RequestMock();
R::store($action); $request->header = [DataMock::getJwt()];
$action = R::dispense('autoaction'); $args = [];
$action->board_id = 1; $args['id'] = 2; // No such task
$action->trigger = 2; // Assigned to user
$action->source_id = 2; // User ID $response = $this->tasks->removeTask($request,
$action->type = 2; // Set category new ResponseMock(), $args);
$action->change_to = 2; // Category ID $this->assertEquals('failure', $response->status);
}
R::store($action);
public function testRemoveTaskForbidden() {
$action = R::dispense('autoaction'); $this->createTask();
$action->board_id = 1; DataMock::CreateBoardAdminUser();
$action->trigger = 2; // Assigned to user
$action->source_id = 2; // User ID $request = new RequestMock();
$action->type = 3; // Add category $request->header = [DataMock::getJwt(2)];
$action->change_to = 0; // Category ID
$args = [];
R::store($action); $args['id'] = 1;
$action = R::dispense('autoaction'); $actual = $this->tasks->removeTask($request,
$action->board_id = 1; new ResponseMock(), $args);
$action->trigger = 3; // Assigned to category $this->assertEquals('Access restricted.',
$action->source_id = 2; // Category ID $actual->alerts[0]['text']);
$action->type = 4; // Set assignee }
$action->change_to = 1; // User ID
private function addActions() {
R::store($action); $action = R::dispense('autoaction');
$action->board_id = 1;
$action = R::dispense('autoaction'); $action->trigger = 1; // Moved to column
$action->board_id = 1; $action->source_id = 2; // Column ID
$action->trigger = 3; // Assigned to category $action->type = 1; // Set color
$action->source_id = 2; // Category ID $action->change_to = '#fff';
$action->type = 5; // Add assignee
$action->change_to = 0; // User ID R::store($action);
R::store($action); $action = R::dispense('autoaction');
$action->board_id = 1;
$action = R::dispense('autoaction'); $action->trigger = 2; // Assigned to user
$action->board_id = 1; $action->source_id = 2; // User ID
$action->trigger = 3; // Assigned to category $action->type = 2; // Set category
$action->source_id = 2; // Category ID $action->change_to = 2; // Category ID
$action->type = 6; // Clear due date
$action->change_to = 0; R::store($action);
R::store($action); $action = R::dispense('autoaction');
$action->board_id = 1;
$action = R::dispense('autoaction'); $action->trigger = 2; // Assigned to user
$action->board_id = 1; $action->source_id = 2; // User ID
$action->trigger = 1; // Moved to column $action->type = 3; // Add category
$action->source_id = 1; // Category ID $action->change_to = 0; // Category ID
$action->type = 6; // Clear due date
$action->change_to = 0; R::store($action);
R::store($action); $action = R::dispense('autoaction');
$action->board_id = 1;
$action = R::dispense('autoaction'); $action->trigger = 3; // Assigned to category
$action->board_id = 1; $action->source_id = 2; // Category ID
$action->trigger = 4; // Points changed $action->type = 4; // Set assignee
$action->source_id = 0; $action->change_to = 1; // User ID
$action->type = 7; // Alter color by points
$action->change_to = 0; R::store($action);
R::store($action); $action = R::dispense('autoaction');
} $action->board_id = 1;
$action->trigger = 3; // Assigned to category
private function getTaskData() { $action->source_id = 2; // Category ID
$data = new stdClass(); $action->type = 5; // Add assignee
$action->change_to = 0; // User ID
$data->title = 'task';
$data->description = 'the words'; R::store($action);
$data->column_id = 1;
$data->color = ''; $action = R::dispense('autoaction');
$data->due_date = null; $action->board_id = 1;
$data->points = null; $action->trigger = 3; // Assigned to category
$data->position = 0; $action->source_id = 2; // Category ID
$data->comments = []; $action->type = 6; // Clear due date
$data->attachments = []; $action->change_to = 0;
$data->assignees = [];
$data->categories = []; R::store($action);
return $data; $action = R::dispense('autoaction');
} $action->board_id = 1;
$action->trigger = 1; // Moved to column
private function createTask() { $action->source_id = 1; // Category ID
$user = R::load('user', 1); $action->type = 6; // Clear due date
$action->change_to = 0;
$task = R::dispense('task');
$task->title = 'test'; R::store($action);
$task->sharedUserList[] = $user;
$action = R::dispense('autoaction');
$column = R::dispense('column'); $action->board_id = 1;
$column->xownTaskList[] = $task; $action->trigger = 4; // Points changed
$column2 = R::dispense('column'); $action->source_id = 0;
$action->type = 7; // Alter color by points
$category = R::dispense('category'); $action->change_to = 0;
$category2 = R::dispense('category');
R::store($action);
$board = R::dispense('board'); }
$board->xownColumnList[] = $column;
$board->xownColumnList[] = $column2; private function getTaskData() {
$board->xownCategoryList[] = $category; $data = new stdClass();
$board->xownCategoryList[] = $category2;
$data->title = 'task';
R::store($board); $data->description = 'the words';
} $data->column_id = 1;
$data->color = '';
$data->due_date = null;
$data->points = null;
$data->position = 0;
$data->comments = [];
$data->attachments = [];
$data->assignees = [];
$data->categories = [];
return $data;
}
private function createTask() {
$user = R::load('user', 1);
$task = R::dispense('task');
$task->title = 'test';
$task->sharedUserList[] = $user;
$column = R::dispense('column');
$column->xownTaskList[] = $task;
$column2 = R::dispense('column');
$category = R::dispense('category');
$category2 = R::dispense('category');
$board = R::dispense('board');
$board->xownColumnList[] = $column;
$board->xownColumnList[] = $column2;
$board->xownCategoryList[] = $category;
$board->xownCategoryList[] = $category2;
R::store($board);
}
} }

View File

@ -3,546 +3,546 @@ require_once __DIR__ . '/../Mocks.php';
use RedBeanPHP\R; use RedBeanPHP\R;
class UsersTest extends PHPUnit\Framework\TestCase { class UsersTest extends PHPUnit\Framework\TestCase {
private $users; private $users;
public static function setupBeforeClass() { public static function setUpBeforeClass(): void {
try { try {
R::setup('sqlite:tests.db'); R::setup('sqlite:tests.db');
} catch (Exception $ex) { } catch (Exception $ex) {
}
} }
}
public function setUp() { public function setUp(): void {
R::nuke(); R::nuke();
Auth::CreateInitialAdmin(new ContainerMock()); Auth::CreateInitialAdmin(new ContainerMock());
$this->users = new Users(new ContainerMock()); $this->users = new Users(new ContainerMock());
} }
public function testGetAllUsers() { public function testGetAllUsers() {
$this->createUser(); $this->createUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->users->getAllUsers($request, $actual = $this->users->getAllUsers($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals(2, count($actual->data[1])); $this->assertEquals(2, count($actual->data[1]));
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$this->users = new Users(new ContainerMock()); $this->users = new Users(new ContainerMock());
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(3)]; $request->header = [DataMock::GetJwt(3)];
$actual = $this->users->getAllUsers($request, $actual = $this->users->getAllUsers($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('error', $actual->alerts[0]['type']); $this->assertEquals('error', $actual->alerts[0]['type']);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetUser() { public function testGetUser() {
$this->createUser(); $this->createUser();
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->users->getUser($request, $actual = $this->users->getUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
$this->assertEquals(2, count($actual->data)); $this->assertEquals(2, count($actual->data));
} }
public function testGetUserUnprivileged() { public function testGetUserUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->users->getUser($request, $actual = $this->users->getUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetUserNotFound() { public function testGetUserNotFound() {
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->users->getUser($request, $actual = $this->users->getUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('No user found for ID 2.', $this->assertEquals('No user found for ID 2.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testGetUserForbidden() { public function testGetUserForbidden() {
$this->createUser(); $this->createUser();
DataMock::CreateStandardUser('nono'); DataMock::CreateStandardUser('nono');
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(3)]; $request->header = [DataMock::GetJwt(3)];
$this->users = new Users(new ContainerMock()); $this->users = new Users(new ContainerMock());
$actual = $this->users->getUser($request, $actual = $this->users->getUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddUser() { public function testAddUser() {
$board = R::dispense('board'); $board = R::dispense('board');
R::store($board); R::store($board);
$user = $this->getUserData(); $user = $this->getUserData();
$user->default_board_id = $board->id; $user->default_board_id = $board->id;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $user; $request->payload = $user;
$actual = $this->users->addUser($request, $actual = $this->users->addUser($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('success', $actual->status); $this->assertEquals('success', $actual->status);
} }
public function testAddDuplicateUser() { public function testAddDuplicateUser() {
$user = $this->getUserData(); $user = $this->getUserData();
$user->username = 'admin'; $user->username = 'admin';
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $user; $request->payload = $user;
$actual = $this->users->addUser($request, $actual = $this->users->addUser($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
public function testAddUserUnprivileged() { public function testAddUserUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$actual = $this->users->addUser($request, $actual = $this->users->addUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testAddBadUser() { public function testAddBadUser() {
$request = new RequestMock(); $request = new RequestMock();
$request->invalidPayload = true; $request->invalidPayload = true;
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$response = $this->users->addUser($request, $response = $this->users->addUser($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
$this->assertEquals('error', $response->alerts[0]['type']); $this->assertEquals('error', $response->alerts[0]['type']);
} }
public function testUpdateUser() { public function testUpdateUser() {
$this->createUser(); $this->createUser();
$user = $this->getUserData(); $user = $this->getUserData();
$user->id = 2; $user->id = 2;
$user->username = 'newname'; $user->username = 'newname';
$user->default_board_id = 1; $user->default_board_id = 1;
$user->password = 'test'; $user->password = 'test';
$user->boardAccess = [2]; $user->boardAccess = [2];
$args = []; $args = [];
$args['id'] = $user->id; $args['id'] = $user->id;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $user; $request->payload = $user;
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$response = $this->users->updateUser($request, $response = $this->users->updateUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $response->status); $this->assertEquals('success', $response->status);
} }
public function testUpdateUserRemoveDefaultBoard() { public function testUpdateUserRemoveDefaultBoard() {
$this->createUser(); $this->createUser();
$user = $this->getUserData(); $user = $this->getUserData();
$user->id = 2; $user->id = 2;
$user->boardAccess = [1]; $user->boardAccess = [1];
$args = []; $args = [];
$args['id'] = $user->id; $args['id'] = $user->id;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $user; $request->payload = $user;
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$response = $this->users->updateUser($request, $response = $this->users->updateUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $response->status); $this->assertEquals('success', $response->status);
} }
public function testUpdateUserPassword() { public function testUpdateUserPassword() {
$this->createUser(); $this->createUser();
$board = R::dispense('board'); $board = R::dispense('board');
R::store($board); R::store($board);
$user = $this->getUserData(); $user = $this->getUserData();
$user->id = 2; $user->id = 2;
$user->new_password = 'updated'; $user->new_password = 'updated';
$user->old_password = 'test'; $user->old_password = 'test';
$user->default_board_id = 2; $user->default_board_id = 2;
$user->boardAccess = []; $user->boardAccess = [];
$args = []; $args = [];
$args['id'] = $user->id; $args['id'] = $user->id;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $user; $request->payload = $user;
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$response = $this->users->updateUser($request, $response = $this->users->updateUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $response->status); $this->assertEquals('success', $response->status);
} }
public function testUpdateUserBadPassword() { public function testUpdateUserBadPassword() {
$this->createUser(); $this->createUser();
$user = $this->getUserData(); $user = $this->getUserData();
$user->id = 2; $user->id = 2;
$user->new_password = 'updated'; $user->new_password = 'updated';
$user->old_password = 'wrong'; $user->old_password = 'wrong';
$args = []; $args = [];
$args['id'] = $user->id; $args['id'] = $user->id;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $user; $request->payload = $user;
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$response = $this->users->updateUser($request, $response = $this->users->updateUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
} }
public function testUpdateUserInvalid() { public function testUpdateUserInvalid() {
$this->createUser(); $this->createUser();
$user = $this->getUserData(); $user = $this->getUserData();
$user->id = 2; $user->id = 2;
$user->username = 'newname'; $user->username = 'newname';
$user->default_board_id = 1; $user->default_board_id = 1;
$args = []; $args = [];
$args['id'] = $user->id; $args['id'] = $user->id;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->invalidPayload = true; $request->invalidPayload = true;
$response = $this->users->updateUser($request, $response = $this->users->updateUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('error', $response->alerts[0]['type']); $this->assertEquals('error', $response->alerts[0]['type']);
} }
public function testUpdateUserUnprivileged() { public function testUpdateUserUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->users->updateUser($request, $actual = $this->users->updateUser($request,
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testUpdateUserRestricted() { public function testUpdateUserRestricted() {
$this->createUser(); $this->createUser();
$user = $this->getUserData(); $user = $this->getUserData();
$user->id = 1; $user->id = 1;
$args = []; $args = [];
$args['id'] = $user->id; $args['id'] = $user->id;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$request->payload = $user; $request->payload = $user;
$actual = $this->users->updateUser($request, $actual = $this->users->updateUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $actual->status); $this->assertEquals('failure', $actual->status);
} }
public function testUpdateUserBadId() { public function testUpdateUserBadId() {
$this->createUser(); $this->createUser();
$user = $this->getUserData(); $user = $this->getUserData();
$user->id = 2; $user->id = 2;
$user->username = 'newname'; $user->username = 'newname';
$user->default_board_id = 1; $user->default_board_id = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$request->payload = $user; $request->payload = $user;
$args = []; $args = [];
$args['id'] = $user->id + 1; $args['id'] = $user->id + 1;
$response = $this->users->updateUser($request, $response = $this->users->updateUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
} }
public function testUpdateUserNameInUse() { public function testUpdateUserNameInUse() {
$this->createUser(); $this->createUser();
$user = $this->getUserData(); $user = $this->getUserData();
$user->id = 2; $user->id = 2;
$user->username = 'admin'; $user->username = 'admin';
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$request->payload = $user; $request->payload = $user;
$args = []; $args = [];
$args['id'] = $user->id; $args['id'] = $user->id;
$response = $this->users->updateUser($request, $response = $this->users->updateUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
} }
public function testUpdateUserOptions() { public function testUpdateUserOptions() {
$this->createUser(); $this->createUser();
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$opts = $this->getUserOptionData(); $opts = $this->getUserOptionData();
$opts->id = 2; $opts->id = 2;
$opts->new_tasks_at_bottom = false; $opts->new_tasks_at_bottom = false;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $opts; $request->payload = $opts;
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$response = $this->users->updateUserOptions($request, $response = $this->users->updateUserOptions($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $response->status); $this->assertEquals('success', $response->status);
} }
public function testUpdateUserOptionsRestricted() { public function testUpdateUserOptionsRestricted() {
$this->createUser(); $this->createUser();
$args = []; $args = [];
$args['id'] = 1; $args['id'] = 1;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$this->users = new Users(new ContainerMock()); $this->users = new Users(new ContainerMock());
$response = $this->users->updateUserOptions($request, $response = $this->users->updateUserOptions($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
$this->assertEquals('Access restricted.', $this->assertEquals('Access restricted.',
$response->alerts[0]['text']); $response->alerts[0]['text']);
} }
public function testUpdateUserOptionsUnprivileged() { public function testUpdateUserOptionsUnprivileged() {
DataMock::createUnprivilegedUser(); DataMock::createUnprivilegedUser();
$data = $this->getUserOptionData(); $data = $this->getUserOptionData();
$data->id = 2; $data->id = 2;
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $data; $request->payload = $data;
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$response = $this->users->updateUserOptions($request, $response = $this->users->updateUserOptions($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
} }
public function testUpdateUserOptionsBadId() { public function testUpdateUserOptionsBadId() {
$this->createUser(); $this->createUser();
$data = new stdClass(); $data = new stdClass();
$data->id = 2; // No such user options $data->id = 2; // No such user options
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $data; $request->payload = $data;
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$response = $this->users->updateUserOptions($request, $response = $this->users->updateUserOptions($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
} }
public function testToggleCollapsed() { public function testToggleCollapsed() {
$this->createUser(); $this->createUser();
$data = new stdClass(); $data = new stdClass();
$data->id = 1; $data->id = 1;
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $data; $request->payload = $data;
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
// Collapse the column // Collapse the column
$response = $this->users->toggleCollapsed($request, $response = $this->users->toggleCollapsed($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $response->status); $this->assertEquals('success', $response->status);
// Expand the column // Expand the column
$response = $this->users->toggleCollapsed($request, $response = $this->users->toggleCollapsed($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('success', $response->status); $this->assertEquals('success', $response->status);
} }
public function testToggleCollapsedNoAccess() { public function testToggleCollapsedNoAccess() {
$response = $this->users->toggleCollapsed(new RequestMock(), $response = $this->users->toggleCollapsed(new RequestMock(),
new ResponseMock(), null); new ResponseMock(), null);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
$data = new stdClass(); $data = new stdClass();
$data->id = 1; $data->id = 1;
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$request = new RequestMock(); $request = new RequestMock();
$request->payload = $data; $request->payload = $data;
$request->header = [DataMock::GetJwt(1)]; $request->header = [DataMock::GetJwt(1)];
$response = $this->users->toggleCollapsed($request, $response = $this->users->toggleCollapsed($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
} }
public function testRemoveUser() { public function testRemoveUser() {
$this->createUser(); $this->createUser();
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$actual = $this->users->removeUser($request, $actual = $this->users->removeUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('User tester removed.', $this->assertEquals('User tester removed.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemoveUserUnprivileged() { public function testRemoveUserUnprivileged() {
DataMock::CreateUnprivilegedUser(); DataMock::CreateUnprivilegedUser();
$args = []; $args = [];
$args['id'] = 2; $args['id'] = 2;
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt(2)]; $request->header = [DataMock::GetJwt(2)];
$actual = $this->users->removeUser($request, $actual = $this->users->removeUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('Insufficient privileges.', $this->assertEquals('Insufficient privileges.',
$actual->alerts[0]['text']); $actual->alerts[0]['text']);
} }
public function testRemoveBadUser() { public function testRemoveBadUser() {
$args = []; $args = [];
$args['id'] = 2; // No such user $args['id'] = 2; // No such user
$request = new RequestMock(); $request = new RequestMock();
$request->header = [DataMock::GetJwt()]; $request->header = [DataMock::GetJwt()];
$response = $this->users->removeUser($request, $response = $this->users->removeUser($request,
new ResponseMock(), $args); new ResponseMock(), $args);
$this->assertEquals('failure', $response->status); $this->assertEquals('failure', $response->status);
} }
private function getUserData() { private function getUserData() {
$data = new stdClass(); $data = new stdClass();
$data->security_level = SecurityLevel::USER; $data->security_level = SecurityLevel::USER;
$data->username = 'tester'; $data->username = 'tester';
$data->email = ''; $data->email = '';
$data->default_board_id = 0; $data->default_board_id = 0;
$data->user_option_id = 0; $data->user_option_id = 0;
$data->last_login = 0; $data->last_login = 0;
$data->active_token = ''; $data->active_token = '';
$data->password = 'test'; $data->password = 'test';
$data->password_verify = 'test'; $data->password_verify = 'test';
return $data; return $data;
} }
private function getUserOptionData() { private function getUserOptionData() {
$data = new stdClass(); $data = new stdClass();
$data->new_tasks_at_bottom = true; $data->new_tasks_at_bottom = true;
$data->show_animations = true; $data->show_animations = true;
$data->show_assignee = true; $data->show_assignee = true;
$data->multiple_tasks_per_row = false; $data->multiple_tasks_per_row = false;
$data->language = "en"; $data->language = "en";
return $data; return $data;
} }
private function createUser() { private function createUser() {
$opts = R::dispense('useroption'); $opts = R::dispense('useroption');
R::store($opts); R::store($opts);
$user = R::dispense('user'); $user = R::dispense('user');
$user->security_level = SecurityLevel::USER; $user->security_level = SecurityLevel::USER;
$user->username = 'tester'; $user->username = 'tester';
$user->default_board_id = 1; $user->default_board_id = 1;
$user->password_hash = password_hash('test', PASSWORD_BCRYPT); $user->password_hash = password_hash('test', PASSWORD_BCRYPT);
$user->user_option_id = $opts->id; $user->user_option_id = $opts->id;
$admin = R::load('user', 1); $admin = R::load('user', 1);
$board = R::dispense('board'); $board = R::dispense('board');
$board->sharedUserList[] = $admin; $board->sharedUserList[] = $admin;
$board->sharedUserList[] = $user; $board->sharedUserList[] = $user;
R::store($board); R::store($board);
} }
} }

View File

@ -1,6 +1,6 @@
<phpunit> <phpunit>
<testsuites> <testsuites>
<testsuite> <testsuite name="api">
<directory>./</directory> <directory>./</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>

View File

@ -82,7 +82,7 @@ describe('ApiInterceptor', () => {
it('handles errors and clears the JWT', it('handles errors and clears the JWT',
inject([HttpClient, HttpTestingController], inject([HttpClient, HttpTestingController],
(http: HttpClient, httpMock: HttpTestingController) => { (http: HttpClient, httpMock: HttpTestingController) => {
sessionStorage.setItem('taskboard.jwt', 'fake'); sessionStorage.setItem('taskboard.jwt', null);
http.get('').subscribe(response => { http.get('').subscribe(response => {
expect(response).toBeTruthy(); expect(response).toBeTruthy();

View File

@ -41,180 +41,186 @@ describe('BoardService', () => {
expect(service).toBeTruthy(); expect(service).toBeTruthy();
}); });
// it('lets the active board get updated', () => { it('lets the active board get updated', () => {
// let changed = false; let changed = false;
//
// service.activeBoardChanged.subscribe(() => (changed = true)); service.activeBoardChanged.subscribe(() => (changed = true));
//
// service.updateActiveBoard(<any>{}); service.updateActiveBoard(<any>{});
//
// expect(changed).toEqual(true); expect(changed).toEqual(true);
// }); });
//
// it('gets all boards', () => { it('gets all boards', () => {
// service.getBoards().subscribe(response => { service.getBoards().subscribe(response => {
// expect(response.data.length).toEqual(0); expect(response.data.length).toEqual(0);
// }); });
//
// testCall('api/boards', 'GET'); testCall('api/boards', 'GET');
// }); });
//
// it('handles errors when getting all boards', () => { it('converts markdown', () => {
// service.getBoards().subscribe(() => {}, response => { const actual = service.convertMarkdown('# Test');
// expect(response.alerts.length).toEqual(1);
// }); expect(actual.html).toEqual('<h1 id="test">Test</h1>\n');
// });
// testCall('api/boards', 'GET', true);
// }); it('handles errors when getting all boards', () => {
// service.getBoards().subscribe(() => {}, response => {
// it('toggles the collapsed state of a column', () => { expect(response.alerts.length).toEqual(1);
// service.toggleCollapsed(1, 1).subscribe(response => { });
// expect(response.data.length).toEqual(0);
// }); testCall('api/boards', 'GET', true);
// });
// testCall('api/users/1/cols', 'POST');
// }); it('toggles the collapsed state of a column', () => {
// service.toggleCollapsed(1, 1).subscribe(response => {
// it('handles error when toggling collapse', () => { expect(response.data.length).toEqual(0);
// service.toggleCollapsed(1, 1).subscribe(() => {}, response => { });
// expect(response.alerts.length).toEqual(1);
// }); testCall('api/users/1/cols', 'POST');
// });
// testCall('api/users/1/cols', 'POST', true);
// }); it('handles error when toggling collapse', () => {
// service.toggleCollapsed(1, 1).subscribe(() => {}, response => {
// it('updates a board', () => { expect(response.alerts.length).toEqual(1);
// service.updateBoard(<any>{ id: 1 }).subscribe(response => { });
// expect(response.data.length).toEqual(0);
// }); testCall('api/users/1/cols', 'POST', true);
// });
// testCall('api/boards/1', 'POST');
// }); it('updates a board', () => {
// service.updateBoard(<any>{ id: 1 }).subscribe(response => {
// it('handles errors on board update', () => { expect(response.data.length).toEqual(0);
// service.updateBoard(<any>{ id: 1 }).subscribe(() => {}, response => { });
// expect(response.alerts.length).toEqual(1);
// }); testCall('api/boards/1', 'POST');
// });
// testCall('api/boards/1', 'POST', true);
// }); it('handles errors on board update', () => {
// service.updateBoard(<any>{ id: 1 }).subscribe(() => {}, response => {
// it('updates a column', () => { expect(response.alerts.length).toEqual(1);
// service.updateColumn(<any>{ id: 1 }).subscribe(response => { });
// expect(response.data.length).toEqual(0);
// }); testCall('api/boards/1', 'POST', true);
// });
// testCall('api/columns/1', 'POST');
// }); it('updates a column', () => {
// service.updateColumn(<any>{ id: 1 }).subscribe(response => {
// it('handles errors on column update', () => { expect(response.data.length).toEqual(0);
// service.updateColumn(<any>{ id: 1 }).subscribe(() => {}, response => { });
// expect(response.alerts.length).toEqual(1);
// }); testCall('api/columns/1', 'POST');
// });
// testCall('api/columns/1', 'POST', true);
// }); it('handles errors on column update', () => {
// service.updateColumn(<any>{ id: 1 }).subscribe(() => {}, response => {
// it('adds a task', () => { expect(response.alerts.length).toEqual(1);
// service.addTask(<any>{}).subscribe(response => { });
// expect(response.data.length).toEqual(0);
// }); testCall('api/columns/1', 'POST', true);
// });
// testCall('api/tasks', 'POST');
// }); it('adds a task', () => {
// service.addTask(<any>{}).subscribe(response => {
// it('handles errors on task add', () => { expect(response.data.length).toEqual(0);
// service.addTask(<any>{}).subscribe(() => {}, response => { });
// expect(response.alerts.length).toEqual(1);
// }); testCall('api/tasks', 'POST');
// });
// testCall('api/tasks', 'POST', true);
// }); it('handles errors on task add', () => {
// service.addTask(<any>{}).subscribe(() => {}, response => {
// it('updates a task', () => { expect(response.alerts.length).toEqual(1);
// service.updateTask(<any>{ id: 1 }).subscribe(response => { });
// expect(response.data.length).toEqual(0);
// }); testCall('api/tasks', 'POST', true);
// });
// testCall('api/tasks/1', 'POST');
// }); it('updates a task', () => {
// service.updateTask(<any>{ id: 1 }).subscribe(response => {
// it('handles errors on task update', () => { expect(response.data.length).toEqual(0);
// service.updateTask(<any>{ id: 1 }).subscribe(() => {}, response => { });
// expect(response.alerts.length).toEqual(1);
// }); testCall('api/tasks/1', 'POST');
// });
// testCall('api/tasks/1', 'POST', true);
// }); it('handles errors on task update', () => {
// service.updateTask(<any>{ id: 1 }).subscribe(() => {}, response => {
// it('removes a task', () => { expect(response.alerts.length).toEqual(1);
// service.removeTask(1).subscribe(response => { });
// expect(response.data.length).toEqual(0);
// }); testCall('api/tasks/1', 'POST', true);
// });
// testCall('api/tasks/1', 'DELETE');
// }); it('removes a task', () => {
// service.removeTask(1).subscribe(response => {
// it('handles errors on task removal', () => { expect(response.data.length).toEqual(0);
// service.removeTask(1).subscribe(() => {}, response => { });
// expect(response.alerts.length).toEqual(1);
// }); testCall('api/tasks/1', 'DELETE');
// });
// testCall('api/tasks/1', 'DELETE', true);
// }); it('handles errors on task removal', () => {
// service.removeTask(1).subscribe(() => {}, response => {
// it('gets task activity', () => { expect(response.alerts.length).toEqual(1);
// service.getTaskActivity(1).subscribe(response => { });
// expect(response.data.length).toEqual(0);
// }); testCall('api/tasks/1', 'DELETE', true);
// });
// testCall('api/activity/task/1', 'GET');
// }); it('gets task activity', () => {
// service.getTaskActivity(1).subscribe(response => {
// it('handles errors on get task activity', () => { expect(response.data.length).toEqual(0);
// service.getTaskActivity(1).subscribe(() => {}, response => { });
// expect(response.alerts.length).toEqual(1);
// }); testCall('api/activity/task/1', 'GET');
// });
// testCall('api/activity/task/1', 'GET', true);
// }); it('handles errors on get task activity', () => {
// service.getTaskActivity(1).subscribe(() => {}, response => {
// it('updates a comment', () => { expect(response.alerts.length).toEqual(1);
// service.updateComment(<any>{ id: 1 }).subscribe(response => { });
// expect(response.data.length).toEqual(0);
// }); testCall('api/activity/task/1', 'GET', true);
// });
// testCall('api/comments/1', 'POST');
// }); it('updates a comment', () => {
// service.updateComment(<any>{ id: 1 }).subscribe(response => {
// it('handles errors on comment update', () => { expect(response.data.length).toEqual(0);
// service.updateComment(<any>{ id: 1 }).subscribe(() => {}, response => { });
// expect(response.alerts.length).toEqual(1);
// }); testCall('api/comments/1', 'POST');
// });
// testCall('api/comments/1', 'POST', true);
// }); it('handles errors on comment update', () => {
// service.updateComment(<any>{ id: 1 }).subscribe(() => {}, response => {
// it('removes a comment', () => { expect(response.alerts.length).toEqual(1);
// service.removeComment(1).subscribe(response => { });
// expect(response.data.length).toEqual(0);
// }); testCall('api/comments/1', 'POST', true);
// });
// testCall('api/comments/1', 'DELETE');
// }); it('removes a comment', () => {
// service.removeComment(1).subscribe(response => {
// it('handles errors on comment removal', () => { expect(response.data.length).toEqual(0);
// service.removeComment(1).subscribe(() => {}, response => { });
// expect(response.alerts.length).toEqual(1);
// }); testCall('api/comments/1', 'DELETE');
// });
// testCall('api/comments/1', 'DELETE', true);
// }); it('handles errors on comment removal', () => {
// service.removeComment(1).subscribe(() => {}, response => {
// it('refreshes the API token', () => { expect(response.alerts.length).toEqual(1);
// service.refreshToken(); });
// testCall('api/refresh', 'POST');
// }); testCall('api/comments/1', 'DELETE', true);
});
it('refreshes the API token', () => {
service.refreshToken();
testCall('api/refresh', 'POST');
});
}); });

View File

@ -44,6 +44,8 @@ describe('ContextMenu', () => {
it('captures parent oncontextmenu events', () => { it('captures parent oncontextmenu events', () => {
const parentElement = component.el.nativeElement.parentElement; const parentElement = component.el.nativeElement.parentElement;
component.el.nativeElement.appendChild(document.createElement('div'));
expect(parentElement.oncontextmenu).toEqual(jasmine.any(Function)); expect(parentElement.oncontextmenu).toEqual(jasmine.any(Function));
parentElement.oncontextmenu({ parentElement.oncontextmenu({

View File

@ -10,7 +10,7 @@
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"target": "es2015", "target": "es5",
"typeRoots": [ "typeRoots": [
"node_modules/@types" "node_modules/@types"
], ],