Compare commits

...

382 Commits

Author SHA1 Message Date
Graham Campbell
47bc73f9e9 Release v2.3.12 2017-06-28 23:41:31 +01:00
Graham Campbell
81fc6479d5 Revert to original travis config 2017-06-28 22:53:06 +01:00
Graham Campbell
c9ca99990c Update .travis.yml 2017-06-28 22:49:07 +01:00
Graham Campbell
e0d35d6e64 Merge branch '2.3' of https://github.com/CachetHQ/Cachet into 2.3 2017-06-28 22:39:55 +01:00
Graham Campbell
813be34f25 Upgraded dependencies 2017-06-28 22:39:41 +01:00
Graham Campbell
900723105b Update .travis.yml 2017-06-28 22:38:44 +01:00
Graham Campbell
8012a201b9 Update .travis.yml 2017-06-28 22:00:28 +01:00
Graham Campbell
706e7765e1 Update .travis.yml 2017-06-28 21:52:45 +01:00
Guido Hendriks
bcff3d0730 Improve testCreateSubscriberWithSubscriptions test case (#2487) 2017-06-28 21:45:27 +01:00
James Brooks
db5bf005bc Apply fixes from StyleCI (#2606)
[ci skip] [skip ci]
2017-06-28 20:52:41 +01:00
James Brooks
dac88efce6 Merge pull request #2601 from Daniel15/2.3-with-patches
Backport API endpoint to get system status to v2.3
2017-06-28 20:52:08 +01:00
James Brooks
38ef2630ee Add system status to /status endpoint 2017-06-20 17:18:38 +10:00
James Brooks
c215ad4111 Implement the system status api endpoint. Closes #1936 2017-06-20 17:09:03 +10:00
James Brooks
c899839a7f Back to dev 2017-04-12 23:05:32 +01:00
James Brooks
2e5e8b1545 Bump version to 2.3.11 2017-04-12 23:05:09 +01:00
James Brooks
7e556d5dba Apply fixes from StyleCI (#2486)
[ci skip] [skip ci]
2017-04-12 23:04:21 +01:00
James Brooks
331e176fbc Updated deps 2017-03-25 11:42:25 +00:00
James Brooks
bde0abe472 Fix handling of individual component subscriptions via API 2016-09-21 18:35:58 +01:00
James Brooks
f2bf2eff68 Test creating a subscriber returns subscriptions 2016-09-21 18:17:22 +01:00
James Brooks
50ab96fc4f Load subscriptions when creating a subscriber 2016-09-21 18:16:08 +01:00
James Brooks
3cbee1302e Update deps 2016-09-21 18:08:56 +01:00
Graham Campbell
8d74923e3f Back to dev 2016-08-11 12:16:50 +01:00
Graham Campbell
dbf6fe528c Release v2.3.10 2016-08-11 12:16:14 +01:00
Graham Campbell
2a9d1b4897 Backport new command tests 2016-08-11 12:09:28 +01:00
Graham Campbell
c2192d7ef7 Backport readme changes 2016-08-11 12:03:28 +01:00
Graham Campbell
1b580360ac Updated deps 2016-08-10 21:04:17 +01:00
James Brooks
15407ad9a3 Remove dump and die 2016-08-10 20:28:32 +01:00
James Brooks
14127de3ca Back to dev 2016-08-07 22:05:34 +01:00
James Brooks
57988f0009 Release v2.3.9 2016-08-07 22:05:16 +01:00
Graham Campbell
7ac7344b5e Updated dep 2016-08-07 22:01:00 +01:00
James Brooks
472dcfe85a Merge pull request #2036 from CachetHQ/analysis-8KggM3
Applied fixes from StyleCI

[ci skip] [skip ci]
2016-08-07 21:10:17 +01:00
James Brooks
77409abc5a Applied fixes from StyleCI
[ci skip] [skip ci]
2016-08-07 16:10:12 -04:00
James Brooks
7548ae7e58 Disable fixedStepSize 2016-08-07 21:09:47 +01:00
James Brooks
074d3457c6 Fix Italian translation issues. Closes #2034 2016-08-07 19:45:13 +01:00
James Brooks
205748b182 Improve metric labels 2016-08-07 19:22:19 +01:00
James Brooks
1f4bee67d6 Back to dev 2016-08-05 07:52:10 +01:00
James Brooks
2f0df5ab7b Bump version 2016-08-05 07:51:44 +01:00
James Brooks
8fe75f7112 Update deps 2016-08-05 07:51:25 +01:00
Graham Campbell
b3ee389ca5 Merge pull request #2021 from CachetHQ/require-xml
Require the ext-xml extension
2016-08-02 11:13:29 +01:00
James Brooks
a40bfd12b7 Removed blank line 2016-08-02 11:02:31 +01:00
James Brooks
8d86c5e072 Ignore SQLite databases when backing up the db 2016-08-02 11:00:39 +01:00
James Brooks
20ca9ceade Require the ext-xml extension 2016-08-02 10:55:33 +01:00
Graham Campbell
6c22c52e3a Updated the aws sdk 2016-07-29 12:51:06 -04:00
James Brooks
ecfa9b1f01 Built in production 2016-07-29 12:04:57 -04:00
James Brooks
380048dd53 Updated Chart.js and rebuilt assets 2016-07-29 11:42:08 -04:00
Graham Campbell
ac57936b62 Merge pull request #2015 from CachetHQ/metrics-y-step-size
Set the step size to the metrics places
2016-07-28 15:39:58 -04:00
James Brooks
962b008fb2 Updated deps 2016-07-28 15:19:58 -04:00
James Brooks
5b1dd8ad00 Set the step size to the metrics places. Fixes #2012 2016-07-28 15:17:20 -04:00
James Brooks
c1d8203602 Added missing setup translations. Closes #2010 2016-07-28 15:06:16 -04:00
James Brooks
dc73f62803 Fixes issues with updating using dashboard api. Closes #2014 2016-07-28 10:47:59 -04:00
Graham Campbell
fdbde63143 Back to dev 2016-07-25 22:58:23 +01:00
Graham Campbell
9a9ec833eb Released 2.3.7 2016-07-25 22:57:05 +01:00
James Brooks
136cce2d6d Merge pull request #2008 from CachetHQ/analysis-XZnLDb
Applied fixes from StyleCI

[ci skip] [skip ci]
2016-07-25 21:52:31 +01:00
Graham Campbell
880a2d9cd9 Applied fixes from StyleCI
[ci skip] [skip ci]
2016-07-25 16:52:26 -04:00
James Brooks
aea18a51c2 Sync languages 2016-07-25 21:42:49 +01:00
Graham Campbell
84bedf5c60 Merge pull request #2007 from CachetHQ/revert-1976-cli
Revert "Don't use settings caching in cli"
2016-07-25 19:32:16 +01:00
Graham Campbell
309a3f96ef Revert "Don't use settings caching in cli" 2016-07-25 19:25:05 +01:00
Graham Campbell
acd1ac496d Merge pull request #2003 from CachetHQ/analysis
Run the new static analysis checks in the tests
2016-07-24 14:58:03 +01:00
Graham Campbell
8c273b8579 Run the new static analysis checks in the tests 2016-07-24 14:48:59 +01:00
Graham Campbell
b30a774bed Merge pull request #2002 from CachetHQ/imports
Added missing imports
2016-07-24 14:48:03 +01:00
Graham Campbell
3ea1428c23 Added missing imports 2016-07-24 14:47:02 +01:00
James Brooks
d6ab5efd14 Fixes #1998 2016-07-22 11:08:49 +01:00
James Brooks
a94be2efca Update deps 2016-07-22 09:19:58 +01:00
Graham Campbell
f8505a866d Back to dev 2016-07-18 19:34:29 +01:00
Graham Campbell
27235abab2 Released 2.3.6 2016-07-18 19:34:06 +01:00
Graham Campbell
f70bc7a42f Merge pull request #1986 from CachetHQ/deps
Updated dependencies
2016-07-18 18:51:37 +01:00
Graham Campbell
6cf622ae21 Updated dependencies 2016-07-18 18:46:43 +01:00
James Brooks
dd1a14a438 Back to dev 2016-07-17 16:38:52 +01:00
James Brooks
387818dbb2 Bump version 2016-07-17 16:38:24 +01:00
James Brooks
5d70d244ce Update feed package. Fixes #1973 2016-07-17 16:37:55 +01:00
James Brooks
ddc846d959 Back to dev 2016-07-16 09:54:54 +01:00
James Brooks
6fb6641499 Bump version 2016-07-16 09:54:28 +01:00
James Brooks
d5eb087eca Merge pull request #1976 from CachetHQ/cli
Don't use settings caching in cli
2016-07-15 07:48:26 +01:00
Graham Campbell
fd25edb2f0 Don't use settings caching in cli 2016-07-14 21:31:07 -07:00
Graham Campbell
5879a2cb1a Updated the exceptions package 2016-07-14 11:11:21 -07:00
Graham Campbell
df5b9b89e6 Updated dependencies 2016-07-13 18:25:27 -07:00
James Brooks
47a5569f02 Improve how we work out system status with scheduled maintenance 2016-07-13 14:19:35 +01:00
James Brooks
ca4a72c518 Merge pull request #1960 from PeterDaveHello/bump-node-dependencies
bump nodejs package dependencies
2016-07-13 13:57:49 +01:00
Peter Dave Hello
6e45b5ae88 bump nodejs package dependencies 2016-07-13 20:57:16 +08:00
James Brooks
99dbc2cd6c Merge pull request #1959 from PeterDaveHello/fix-file-permission
remove executable permission from the files don't need it
2016-07-12 09:31:22 +01:00
Peter Dave Hello
2f2ed53b58 remove executable permission from the files don't need it 2016-07-12 16:28:28 +08:00
James Brooks
7e1ead91ed Merge pull request #1958 from PeterDaveHello/image-optimize
optimize png images using zopflipng
2016-07-12 09:23:02 +01:00
Peter Dave Hello
28578474b9 optimize png images using zopflipng 2016-07-12 16:20:14 +08:00
James Brooks
d4e332bf55 Merge pull request #1956 from PeterDaveHello/patch-1
Update README.md
2016-07-12 09:15:37 +01:00
Peter Dave Hello
13f0f67a7b Update README.md
Use svg instead of png to get localized percentage badge for better image quality.
2016-07-12 16:14:54 +08:00
James Brooks
709cac9332 Merge pull request #1953 from CachetHQ/fix-setup
fix setup env writing
2016-07-11 19:03:48 +01:00
Joseph Cohen
e17196d4c9 fix setup env writing 2016-07-11 13:00:00 -05:00
James Brooks
80be140c1f Update ISSUE_TEMPLATE 2016-07-11 09:50:33 +01:00
James Brooks
4f9af8bca0 Remove blank line 2016-07-10 17:04:08 +01:00
James Brooks
af3b7836ff Back to dev 2016-07-10 10:39:42 +01:00
James Brooks
cc4a960ea6 Release v2.3.3 2016-07-10 10:39:19 +01:00
James Brooks
467d29ca11 Use numeric validation for metric points. Fixes #1950 2016-07-10 10:37:00 +01:00
James Brooks
ac1355771c Merge pull request #1947 from CachetHQ/set-locale
Set Date locale
2016-07-08 19:43:04 +01:00
James Brooks
c41adc1019 Override date locale again. Fixes #1727 2016-07-08 19:42:20 +01:00
James Brooks
5b74b2d625 Merge pull request #1949 from CachetHQ/dashboard-api-bus
The dashboard API needs to use the Command Bus
2016-07-08 18:39:38 +01:00
James Brooks
d7835f68ed Merge pull request #1948 from CachetHQ/analysis-zO37Lk
Applied fixes from StyleCI

[ci skip] [skip ci]
2016-07-08 17:42:17 +01:00
James Brooks
a1f1a2b969 Applied fixes from StyleCI
[ci skip] [skip ci]
2016-07-08 12:42:13 -04:00
James Brooks
77394995e4 The dashboard API needs to use the Command Bus. Fixes #1927 2016-07-08 17:42:01 +01:00
James Brooks
9953557497 Back to dev 2016-07-06 21:54:50 +01:00
James Brooks
4dc175b2be Correct dev version 2016-07-06 21:53:15 +01:00
James Brooks
4a2fe00c89 Bump version 2016-07-06 21:52:32 +01:00
James Brooks
db55283b85 Merge pull request #1940 from CachetHQ/analysis-86V7RW
Applied fixes from StyleCI

[ci skip] [skip ci]
2016-07-06 19:14:04 +01:00
James Brooks
5c6440e890 Sync languages 2016-07-06 19:14:21 +01:00
James Brooks
6b299b0a90 Applied fixes from StyleCI
[ci skip] [skip ci]
2016-07-06 14:14:00 -04:00
James Brooks
9b5d4aa7c4 Merge pull request #1939 from CachetHQ/metric-repository-prefix
Metric repository now knows prefixes
2016-07-06 19:03:42 +01:00
James Brooks
1fdda03199 Metric repository now knows prefixes. Fixes #1938 2016-07-06 18:42:31 +01:00
James Brooks
7c846d06ff Updated deps 2016-07-06 18:32:04 +01:00
James Brooks
e34e4381d4 Update VERSION 2016-07-06 18:24:58 +01:00
Graham Campbell
b255f71958 Merge pull request #1934 from odannyc/patch-1
Misspelled 'email'
2016-07-02 10:59:51 +01:00
Danny Carrillo
9560b2a0dc Misspelled 'email' 2016-07-01 13:55:28 -07:00
James Brooks
f8491ceead Back to dev 2016-06-27 22:10:37 +01:00
James Brooks
823fe6e9e2 Bump version 2016-06-27 22:10:13 +01:00
James Brooks
b7e9c07a05 Merge pull request #1924 from CachetHQ/analysis-zDyveB
Applied fixes from StyleCI

[ci skip] [skip ci]
2016-06-27 21:18:57 +01:00
James Brooks
a9d83e1337 Applied fixes from StyleCI
[ci skip] [skip ci]
2016-06-27 16:18:52 -04:00
James Brooks
86cfcb501a Added /admin route redirect to /dashboard 2016-06-27 21:18:44 +01:00
James Brooks
8f68b2a347 Re-compiled assets 2016-06-27 18:39:31 +01:00
Graham Campbell
1423a9bbd0 Updated dependencies 2016-06-27 15:48:35 +01:00
James Brooks
39d302175c Merge pull request #1919 from travelton/group-dashboard-components
Component Grouping in Dashboard
2016-06-27 15:41:01 +01:00
Travis Swientek
fffb4d964c Component Grouping in Dashboard, Spelling Adjustment. 2016-06-27 09:05:34 -05:00
James Brooks
7ba12960dd Added missing events to EventServiceProvider 2016-06-27 12:22:18 +01:00
James Brooks
440f45ed4a Merge pull request #1920 from juniorb2ss/2.3
Fix language name.
2016-06-24 17:42:11 +01:00
Carlos Eduardo
549eaa3382 Fix language name.
Correct name of slugs portuguese.
2016-06-24 12:27:10 -03:00
James Brooks
5305f71c2b Updated deps 2016-06-20 13:49:55 +01:00
James Brooks
bf749c54ee Move helpers.php out of Http namespace 2016-06-17 09:16:23 +01:00
James Brooks
f1e2c1ef68 Merge pull request #1911 from CachetHQ/fix-lastpass
Help password managers
2016-06-15 08:54:50 +01:00
James Brooks
4ab9807cbe Help password managers. Fixes #1910 2016-06-15 08:54:13 +01:00
James Brooks
9f9c9a10c5 Merge pull request #1886 from CachetHQ/wizard-smtp
Wizard email configuration
2016-06-13 20:06:51 +01:00
Joseph Cohen
a5ed3c40a9 Fix write env to work with nulls and change from to address 2016-06-13 20:06:40 +01:00
James Brooks
1fe4789760 Fix spacing 2016-06-13 20:06:39 +01:00
James Brooks
9c1e94c550 Put mail log to bottom 2016-06-13 20:06:39 +01:00
Joseph Cohen
958c9eadc7 Add mail data to wizard 2016-06-13 20:06:39 +01:00
Joseph Cohen
ca3f516457 Add mail driver to the wizard 2016-06-13 20:06:28 +01:00
Graham Campbell
04215fc37a Back to dev 2016-06-11 00:47:41 +01:00
Graham Campbell
8d90357f40 Released 2.3.0 RC6 2016-06-11 00:47:07 +01:00
Graham Campbell
46add8d841 Merge branch '2.2' into 2.3
# Conflicts:
#	VERSION
#	composer.json
#	composer.lock
2016-06-11 00:46:01 +01:00
Graham Campbell
1e695d51b5 Added the aws sdk as a dependency 2016-06-11 00:45:26 +01:00
Graham Campbell
65f864d743 Back to dev 2016-06-11 00:40:45 +01:00
Graham Campbell
bc51aac622 Released 2.2.4 2016-06-11 00:40:10 +01:00
Graham Campbell
3c39ccd5df Updated dependencies 2016-06-11 00:32:51 +01:00
Graham Campbell
cd4a96983e Updated laravel 2016-06-11 00:21:03 +01:00
Graham Campbell
c0d3602a91 Updated dependencies 2016-06-11 00:19:19 +01:00
James Brooks
08175b89e9 Merge pull request #1905 from CachetHQ/analysis-qMPD92
Applied fixes from StyleCI

[ci skip] [skip ci]
2016-06-10 17:27:37 +01:00
James Brooks
ece696d7db Merge pull request #1904 from CachetHQ/analysis-z327Ep
Applied fixes from StyleCI

[ci skip] [skip ci]
2016-06-10 17:27:33 +01:00
James Brooks
e7052f5aff Applied fixes from StyleCI
[ci skip] [skip ci]
2016-06-10 12:27:33 -04:00
James Brooks
ff97327803 Applied fixes from StyleCI
[ci skip] [skip ci]
2016-06-10 12:27:29 -04:00
James Brooks
8dda99c499 Compiled assets 2016-06-10 17:27:26 +01:00
James Brooks
947aa27e40 Move dashboard version check into version api endpoint 2016-06-10 17:27:21 +01:00
James Brooks
1425c15ee8 Don't rely on jQuery to be ready 2016-06-10 17:13:42 +01:00
James Brooks
cdbd2db8e9 Re-compile assets 2016-06-10 13:46:28 +01:00
James Brooks
ef02d77439 Remove the login background 2016-06-10 13:42:13 +01:00
Graham Campbell
88ce241785 Updated dependencies 2016-06-10 08:53:26 +01:00
Graham Campbell
b64dd1e87e Properly fixed metric updating
// cc @jbrooksuk
2016-06-07 11:11:17 +01:00
Graham Campbell
a0b9856d61 Updated deps 2016-06-07 11:07:02 +01:00
James Brooks
d6552982a3 Order by id if order is all the same 2016-06-07 08:54:56 +01:00
James Brooks
42eac92737 Pass update order as null from the dashboard 2016-06-07 08:53:41 +01:00
Graham Campbell
a2cee97f33 Rebuilt assets 2016-06-07 00:13:41 +01:00
Graham Campbell
0541a662ff Fixed path 2016-06-07 00:12:17 +01:00
Graham Campbell
6c9b7a6fde Updated dependencies 2016-06-06 20:32:49 +01:00
James Brooks
ff9a2802f8 Back to dev 2016-06-06 11:20:10 +01:00
James Brooks
06ca0ffb21 Released v2.2.3 2016-06-06 11:19:28 +01:00
James Brooks
80e5655be9 Merge pull request #1895 from CachetHQ/str-include
Fix the Str import. Fixes #1749
2016-06-06 11:18:26 +01:00
James Brooks
0b7ca97e53 Fix the Str include 2016-06-06 11:03:09 +01:00
James Brooks
812160839f Support a Redis passwords 2016-06-05 16:52:18 +01:00
James Brooks
27e46d77e6 Publish the cache table migration 2016-06-05 10:16:50 +01:00
James Brooks
a10d12e589 Merge pull request #1869 from CachetHQ/update-login-page
Updated login page
2016-06-04 15:55:40 +01:00
James Brooks
ac7652f3aa Remove unused divs 2016-06-04 15:55:01 +01:00
James Brooks
c63fedefe9 Updated login page 2016-06-04 14:57:16 +01:00
Graham Campbell
c17ef87651 Merge pull request #1891 from CachetHQ/deps
Updated dependencies
2016-06-03 17:23:47 +01:00
Graham Campbell
846f77b054 Updated dependencies 2016-06-03 17:20:14 +01:00
Graham Campbell
d5c29a6c4b Merge pull request #1890 from CachetHQ/fixes
Fixed the integrations
2016-06-03 17:11:00 +01:00
Graham Campbell
31861f020f Fixed typo 2016-06-03 17:10:24 +01:00
Graham Campbell
abf83361e8 Fixed the integrations 2016-06-03 17:07:36 +01:00
James Brooks
3e8801d8d4 Merge pull request #1887 from CachetHQ/analysis-XkYWW1
Applied fixes from StyleCI

[ci skip] [skip ci]
2016-06-03 14:00:33 +01:00
James Brooks
bbc9eb1f81 Applied fixes from StyleCI
[ci skip] [skip ci]
2016-06-03 09:00:29 -04:00
James Brooks
c8b602d349 Handle failing to fetch blog posts. Fixes #1878 2016-06-03 14:00:23 +01:00
James Brooks
715eb02844 Use default_view or view. Fixes #1842 2016-06-03 13:54:26 +01:00
James Brooks
c8f7e92124 Only test the seed command 2016-06-03 10:44:02 +01:00
James Brooks
eae44ee6cb Fix redirecting back to the correct settings page 2016-06-03 10:16:44 +01:00
James Brooks
3fde593a86 Standardise the error message 2016-06-03 10:15:08 +01:00
James Brooks
1527ec8ddc Test all app: commands 2016-06-02 18:40:51 +01:00
Graham Campbell
c90584bda5 Merge pull request #1879 from CachetHQ/deps
Updated dependencies
2016-06-02 10:46:20 +01:00
Graham Campbell
aea3b40c54 Updated dependencies 2016-06-02 10:43:40 +01:00
Graham Campbell
d394e108bd Fix the feed package 2016-06-02 10:37:47 +01:00
Graham Campbell
0df9f01ffe Updated dependencies 2016-06-02 10:30:38 +01:00
James Brooks
9745bb7543 Merge pull request #1876 from CachetHQ/metric-ordering-api
Set an order on metrics via the API
2016-06-02 10:16:35 +01:00
James Brooks
cab030237b Set an order on metrics via the API. Closes #1874 2016-06-02 10:16:23 +01:00
James Brooks
439ac9fe44 Updated dependencies 2016-06-02 09:21:33 +01:00
James Brooks
7eef9467aa Don't go resetting the threshold value 2016-06-02 09:09:03 +01:00
James Brooks
15387b1da8 Allow searching incidents by component_id. Closes #1873 2016-06-02 08:46:59 +01:00
James Brooks
c33d297fa4 Merge pull request #1871 from CachetHQ/analysis-8jlox9
Applied fixes from StyleCI

[ci skip]
2016-06-01 11:50:39 +01:00
James Brooks
c8af103498 Applied fixes from StyleCI
[ci skip]
2016-06-01 06:50:35 -04:00
James Brooks
0e2610eee0 Fix indentation of setting 2016-06-01 11:50:27 +01:00
James Brooks
e201a6ed06 Seed with Patreon page component 2016-06-01 11:50:27 +01:00
James Brooks
1e4d616b88 Merge pull request #1870 from CachetHQ/analysis-8wOpew
Applied fixes from StyleCI

[ci skip]
2016-06-01 11:49:11 +01:00
James Brooks
2de01671e9 Applied fixes from StyleCI
[ci skip]
2016-06-01 06:49:07 -04:00
James Brooks
dd26a3af34 Fix default settings for enable_subscribers 2016-06-01 11:48:59 +01:00
Graham Campbell
4b41395144 Tweak 2016-06-01 11:42:25 +01:00
James Brooks
2dacc71e8a Set the default show_support setting 2016-06-01 08:04:16 +01:00
James Brooks
4869c7ee21 Merge pull request #1867 from CachetHQ/analysis-8maVvE
Applied fixes from StyleCI

[ci skip]
2016-05-31 20:19:00 +01:00
James Brooks
9293dcf0df Applied fixes from StyleCI
[ci skip]
2016-05-31 15:18:56 -04:00
James Brooks
150057ef50 Sync languages 2016-05-31 20:18:39 +01:00
James Brooks
c81f18c3bc Back to dev 2016-05-31 19:31:56 +01:00
James Brooks
42aa437ac2 Released 2.3.0 RC5 2016-05-31 19:31:34 +01:00
James Brooks
6a780314fb Rebuild assets 2016-05-31 19:29:28 +01:00
James Brooks
ffae9cf3d4 Merge pull request #1866 from CachetHQ/analysis-zRP0lp
Applied fixes from StyleCI

[ci skip]
2016-05-31 19:27:14 +01:00
James Brooks
091f59c241 Applied fixes from StyleCI
[ci skip]
2016-05-31 14:27:10 -04:00
James Brooks
288fa6180b Publish the css-inliner config 2016-05-31 19:27:00 +01:00
James Brooks
7ecb546a86 Merge pull request #1865 from CachetHQ/dashboard-feed
Dashboard feed
2016-05-31 19:25:30 +01:00
James Brooks
f7c8dd6254 Merge pull request #1864 from CachetHQ/analysis-XN4p11
Applied fixes from StyleCI

[ci skip]
2016-05-31 19:12:36 +01:00
James Brooks
2adc9d032a Applied fixes from StyleCI
[ci skip]
2016-05-31 14:12:32 -04:00
James Brooks
f53075ec4f Add Patreon link and latest Cachet blog posts to dashboard 2016-05-31 19:12:23 +01:00
James Brooks
0190813012 Dashboard RSS 2016-05-31 18:02:10 +01:00
Graham Campbell
20d187d642 Back to dev 2016-05-29 19:28:42 +01:00
Graham Campbell
91bd9288f5 Released 2.3.0 RC4 2016-05-29 19:28:10 +01:00
Graham Campbell
24df32e9a9 Fixed bad caching 2016-05-29 19:20:56 +01:00
Graham Campbell
fb75ad6902 Include our user agent 2016-05-29 19:17:22 +01:00
Graham Campbell
aa87274378 Another fix 2016-05-29 18:49:40 +01:00
Graham Campbell
696a1126c7 Fixed lang file 2016-05-29 18:47:33 +01:00
Graham Campbell
7c4787a1a7 Merge pull request #1815 from CachetHQ/backers-credits-dashboard
Added credits page
2016-05-29 18:44:52 +01:00
Graham Campbell
74d24b6809 Link the images 2016-05-29 18:40:29 +01:00
Graham Campbell
22226c666f Force size 2016-05-29 18:34:08 +01:00
Graham Campbell
b3244a4639 Fixed import 2016-05-29 18:27:04 +01:00
Graham Campbell
6cf3c4c109 Merge pull request #1861 from CachetHQ/analysis-z4waDG
Applied fixes from StyleCI

[ci skip]
2016-05-29 18:24:17 +01:00
Graham Campbell
8f47bd4a4f Applied fixes from StyleCI
[ci skip]
2016-05-29 13:24:13 -04:00
Graham Campbell
c03f01ca44 Added credits 2016-05-29 18:14:40 +01:00
Graham Campbell
ab0ed775e1 Merge pull request #1860 from CachetHQ/tests
Cleanup travis test config
2016-05-29 12:09:48 +01:00
Graham Campbell
5a600c0e17 Removed old badge 2016-05-29 12:00:59 +01:00
Graham Campbell
601a863fc4 Cleanup tests 2016-05-29 12:00:02 +01:00
Graham Campbell
366eab9cae Merge pull request #1859 from CachetHQ/typo
Fixed typo
2016-05-29 11:13:14 +01:00
Graham Campbell
021c2890d2 Fixed typo 2016-05-29 10:57:45 +01:00
Graham Campbell
2eff325a23 Back to dev 2016-05-28 22:02:11 +01:00
Graham Campbell
d7e70c1870 Released 2.3.0 RC3 2016-05-28 22:01:44 +01:00
Graham Campbell
0311d59e78 Updated dependencies 2016-05-28 20:27:19 +01:00
Graham Campbell
6a6c8d866e Merge pull request #1843 from CachetHQ/improve-emails
Improve email designs, use theme colours
2016-05-28 18:36:55 +01:00
Graham Campbell
11d82a22c3 Merge pull request #1856 from CachetHQ/fix-flash-msg
Fix login flash messages #1850
2016-05-28 18:36:26 +01:00
Joseph Cohen
dcfe55e881 Fix login flash messages #1850 2016-05-28 12:34:52 -05:00
Graham Campbell
494d1a021c Merge pull request #1855 from CachetHQ/proxies
Synced the trusted proxies
2016-05-28 18:32:42 +01:00
Graham Campbell
d95557d683 Synced the trusted proxies 2016-05-28 18:31:41 +01:00
Joe Cohen
48ccc02799 Applied fixes from StyleCI
[ci skip]
2016-05-28 12:30:39 -05:00
Joseph Cohen
f3440389be Improve email content and subject, fix component notification presenter 2016-05-28 12:30:39 -05:00
James Brooks
bad9b1d550 Improve email designs, use theme colours 2016-05-28 12:30:39 -05:00
Graham Campbell
e1d07fdc99 Updated the throttle package again 2016-05-28 16:28:53 +01:00
Graham Campbell
e69395a5ad Merge pull request #1852 from CachetHQ/tests
Added more service provider tests
2016-05-28 16:27:35 +01:00
Graham Campbell
1c10cba5fc Added more service provider tests 2016-05-28 16:19:33 +01:00
Graham Campbell
3a1042e6ae Tweaked the metric repo binding 2016-05-28 16:19:17 +01:00
Graham Campbell
a2a008b108 Updated the throttle package 2016-05-28 16:18:28 +01:00
Graham Campbell
11f8ba81fd Merge pull request #1851 from CachetHQ/throttling
Fix crap login throttling
2016-05-28 15:52:25 +01:00
Graham Campbell
e8d216b671 Enabled the new displayer 2016-05-28 15:50:03 +01:00
Graham Campbell
167d076edc Fix crap login throttling 2016-05-28 15:45:38 +01:00
James Brooks
5c5634d355 We no longer need to pass the app_name in 2016-05-27 11:35:59 +01:00
Graham Campbell
1eea34e832 Updated laravel 2016-05-27 08:57:12 +01:00
James Brooks
3873fd90ba Make sure subscribers are enabled before displaying add subscriber 2016-05-26 11:51:25 +01:00
James Brooks
38a659b08c Back to dev 2016-05-26 11:44:40 +01:00
James Brooks
55b6961a4b Released 2.3.0 RC2 2016-05-26 11:43:39 +01:00
James Brooks
2b87629dca Fix issues with localizations not applying properly 2016-05-26 11:35:00 +01:00
James Brooks
48c4240c38 Removed un-needed equality param 2016-05-26 11:28:52 +01:00
James Brooks
f84793bfab Remove strong tag 2016-05-26 11:22:23 +01:00
James Brooks
0200ac848f Reduce indentation 2016-05-26 11:20:07 +01:00
James Brooks
787bb74b3b Fix trailing "email" string 2016-05-26 11:19:54 +01:00
James Brooks
8f58cdd182 Merge pull request #1831 from CachetHQ/reduce-size-settings-controller
Moved banner handling into a separate method as it's big
2016-05-26 08:43:20 +01:00
James Brooks
8f0691f3bd Merge pull request #1835 from CachetHQ/subscribers
Remove subscribers modal
2016-05-26 08:42:57 +01:00
James Brooks
a81ce21721 Merge pull request #1836 from CachetHQ/lang
Fixed English translations
2016-05-26 08:41:50 +01:00
Graham Campbell
1e0992be6c Restored the validation translations 2016-05-26 08:18:02 +01:00
Graham Campbell
00706ad744 Fixed uppercase letter 2016-05-26 08:16:49 +01:00
Joseph Cohen
31d47cfb35 Remove subscribers modal 2016-05-26 00:50:23 -05:00
James Brooks
7e29f7d363 Moved banner handling into a separate method as it's big 2016-05-25 22:16:41 +01:00
Graham Campbell
71a1ab091f Updated laravel 2016-05-25 21:55:26 +01:00
James Brooks
9e020e5d59 Merge pull request #1830 from CachetHQ/default-enable-subscibers
Enable subscribers by default
2016-05-25 21:20:45 +01:00
James Brooks
5e10f1c777 Enable subscribers by default 2016-05-25 21:20:24 +01:00
Graham Campbell
a2aa116204 Merge pull request #1829 from CachetHQ/fix-config-seeding
Fix clearing of configs when seeding
2016-05-25 20:30:30 +01:00
Graham Campbell
ac65cc56bb I prefer clear 2016-05-25 20:26:58 +01:00
James Brooks
9e3f07742b Fix clearing of configs when seeding 2016-05-25 20:00:33 +01:00
James Brooks
b120c37cbc Fix colour of p and strong tags 2016-05-25 19:23:11 +01:00
James Brooks
6211e7a1f8 Drop the "| Cachet" extra from the site title 2016-05-25 12:23:22 +01:00
Graham Campbell
5d23f83db6 Modified english translations next/previous 2016-05-25 12:21:17 +01:00
Graham Campbell
a2ea9bf10b Merge pull request #1826 from CachetHQ/controller
Avoid the deprecated controller method
2016-05-25 12:17:38 +01:00
Graham Campbell
d6495dec96 Avoid the deprecated controller method 2016-05-25 12:15:42 +01:00
Graham Campbell
5cbe3ba708 Back to dev 2016-05-25 11:39:32 +01:00
Graham Campbell
375d2330de Released 2.3.0 RC1 2016-05-25 11:39:07 +01:00
Graham Campbell
e39a3cde16 Merge pull request #1823 from CachetHQ/settings-cache
Refactored settings caching
2016-05-25 11:34:02 +01:00
Graham Campbell
13d0ff320a Another attempt 2016-05-25 11:28:03 +01:00
Graham Campbell
72577a04b7 Fixed headers 2016-05-25 11:23:48 +01:00
James Brooks
0bf2039e60 Fix some bits 2016-05-25 11:14:16 +01:00
Graham Campbell
414efb0ce7 Fixed typo 2016-05-25 10:59:11 +01:00
Graham Campbell
0500d63654 Merge pull request #1825 from CachetHQ/analysis-X017pj
Applied fixes from StyleCI

[ci skip]
2016-05-25 10:58:44 +01:00
Graham Campbell
3a86862e4f Applied fixes from StyleCI
[ci skip]
2016-05-25 05:58:40 -04:00
Graham Campbell
8df65b2dc3 Finished off the subscriber 2016-05-25 10:58:14 +01:00
Graham Campbell
c3bc8fdd2d Refactor settings caching 2016-05-25 10:55:49 +01:00
Graham Campbell
200cd62dc9 Resolve via class name 2016-05-25 10:55:33 +01:00
Graham Campbell
527e5872df Fixed typo 2016-05-25 10:55:22 +01:00
Graham Campbell
ebaf4e9395 WIP 2016-05-25 10:33:06 +01:00
Graham Campbell
d0afcbffe4 Updated dependencies (#1824) 2016-05-25 10:31:05 +01:00
Graham Campbell
2e9d56d053 Fixed typo 2016-05-25 09:55:07 +01:00
James Brooks
90419e70cf Merge pull request #1821 from CachetHQ/analysis-Xa67jZ
Applied fixes from StyleCI

[ci skip]
2016-05-25 09:52:48 +01:00
Graham Campbell
0566133c12 Cleaned up the release class and provider (#1820) 2016-05-25 09:52:44 +01:00
James Brooks
8e824eb5e0 Applied fixes from StyleCI
[ci skip]
2016-05-25 04:45:43 -04:00
James Brooks
5534cdbdde Sync languages 2016-05-25 09:45:20 +01:00
James Brooks
2db0b011e6 Rebuild assets 2016-05-25 09:28:02 +01:00
gellu
962b40f0e9 Fixed undefined component variable (#1808) 2016-05-24 20:04:47 +01:00
Graham Campbell
96450c476c Merge pull request #1811 from CachetHQ/subscribe-multiple-subscribers
Subscribe multiple subscribers at one time
2016-05-24 20:03:45 +01:00
James Brooks
ba09b3f7cd Merge pull request #1818 from CachetHQ/settings
Renamed "Config" to "Settings"
2016-05-24 18:35:23 +01:00
Graham Campbell
053e3e9477 Renamed "Config" to "Settings" 2016-05-24 17:48:13 +01:00
Graham Campbell
ff939e356e Merge pull request #1816 from CachetHQ/caching
Settings Caching
2016-05-24 17:43:15 +01:00
Graham Campbell
e4d5c53d69 Derp fix 2016-05-24 17:20:23 +01:00
Graham Campbell
4549c51959 Deal with cases when we have no settings properly 2016-05-24 17:12:08 +01:00
Graham Campbell
20e4ae5ff8 Scaled things back 2016-05-24 17:00:48 +01:00
Graham Campbell
77f455651d Work on config caching 2016-05-24 16:44:21 +01:00
Graham Campbell
7a3c231f68 Merge pull request #1812 from CachetHQ/fix-subscriber-dates
Fix subscriber dates
2016-05-24 15:13:04 +01:00
Graham Campbell
0ec77d7652 Merge pull request #1813 from CachetHQ/dashboard-component-status-bug
Fixes toggling of component status box
2016-05-24 15:12:47 +01:00
James Brooks
4f886e15ed Fixes toggling of component status box. Fixes #1792 2016-05-24 15:07:36 +01:00
James Brooks
d3454d8126 Merge pull request #1810 from CachetHQ/allow-user-level-editing
Edit the user level from edit user page
2016-05-24 14:57:42 +01:00
James Brooks
ccc0ebb7d5 Fixes #1805. Subscribers no longer have subscribed_at dates 2016-05-24 14:52:13 +01:00
James Brooks
f6bfb10500 Outdent the template 2016-05-24 14:49:09 +01:00
James Brooks
e241f38dd7 Subscribe multiple subscribers at one time 2016-05-24 14:47:34 +01:00
James Brooks
fb0dd82fe2 Edit the user level from edit user page. Closes #1809 2016-05-24 14:39:59 +01:00
James Brooks
6997e0c451 Merge pull request #1803 from CachetHQ/rename-next-prev
Use only Previous and Next links
2016-05-23 08:20:07 +01:00
James Brooks
2e73d071c9 Use only Previous and Next links. Closes #1801 2016-05-23 08:05:32 +01:00
Graham Campbell
f86f6bbc08 Merge pull request #1799 from CachetHQ/deps
Updated dependencies
2016-05-20 17:51:03 +01:00
Graham Campbell
6c872e60fc Updated dependencies 2016-05-20 17:40:31 +01:00
James Brooks
23f77a7e72 Merge pull request #1798 from CachetHQ/backup
Cleanup the backup process
2016-05-20 15:54:47 +01:00
Graham Campbell
25dd45f47c Cleanup the backup process 2016-05-20 14:49:41 +01:00
Graham Campbell
2680bafd69 Merge pull request #1797 from CachetHQ/specify-component-group-components-order
Order components within group
2016-05-20 14:38:47 +01:00
James Brooks
08c1f105c2 Order components within group. Fixes #1793 2016-05-20 14:31:08 +01:00
James Brooks
c152892610 Merge pull request #1796 from CachetHQ/jbrooksuk-patch-1
Remove Paypal button in favour of Patreon link
2016-05-20 14:03:10 +01:00
James Brooks
d54ead2868 Remove Paypal button in favour of Patreon link 2016-05-20 13:21:49 +01:00
James Brooks
81ae63b2c0 Merge pull request #1795 from vinkla/patch-2
Remove npm dependencies
2016-05-20 12:56:52 +01:00
Vincent Klaiber
9f17c34f50 Remove npm dependencies
This closes #1794
2016-05-20 13:48:39 +02:00
Graham Campbell
d0be61ad96 Updated dependencies 2016-05-13 00:27:00 +01:00
James Brooks
89f99ef663 Merge pull request #1781 from CachetHQ/drop-bower
Remove Bower dependency
2016-05-11 20:38:39 +01:00
James Brooks
9f4270f4a5 Remove Bower dependency. Closes #1780 2016-05-11 20:11:46 +01:00
James Brooks
bda3484cac Merge pull request #1777 from CachetHQ/values
Use pluck rather than lists
2016-05-09 22:04:05 +01:00
James Brooks
aec2ef0198 Use pluck rather than lists 2016-05-09 20:43:24 +01:00
Joe Cohen
651edcc7c5 Merge pull request #1754 from CachetHQ/fix-spanish-cachet-translations
Fix translations in the cachet.php file
2016-05-09 14:08:47 -05:00
James Brooks
d975cd8ca1 Fix spanish translations 2016-05-09 13:30:07 -05:00
James Brooks
c315d04d5d Merge pull request #1773 from CachetHQ/remove-setup-key-generate
Setup doesn't need to generate the key anymore
2016-05-08 17:47:08 +01:00
James Brooks
e77562d469 Setup doesn't need to generate the key anymore 2016-05-08 17:46:29 +01:00
James Brooks
088fd6631d Merge pull request #1771 from CachetHQ/subscribers-api-specify-components
Specify components to subscribe to via API
2016-05-08 16:54:45 +01:00
James Brooks
cc102847f9 Specify components to subscribe to via API. Closes #1685 2016-05-08 16:26:59 +01:00
James Brooks
b88a102629 Merge pull request #1768 from CachetHQ/default-settings
Allow us to set better default settings
2016-05-08 16:24:57 +01:00
James Brooks
452eb653a5 Allow us to set better default settings. Closes #1755 2016-05-08 16:06:11 +01:00
James Brooks
1584883f39 Merge pull request #1767 from TheoBearman/2.3
Moving 'Automatically localise your status page to your visitor's language?' to its correct home
2016-05-07 21:12:30 +01:00
Theo Bearman
595a720d8b Moving 'Automatically localise your status page to your visitor's language?' to its correct home - Fixes #1765 2016-05-07 17:24:58 +01:00
Graham Campbell
2a0decc7c6 Merge pull request #1763 from CachetHQ/composer
Updated packages
2016-05-07 09:27:10 +01:00
James Brooks
d10386821e Updated lockfile 2016-05-07 08:52:26 +01:00
Graham Campbell
8da8e83e48 Updated packages 2016-05-06 22:46:51 +01:00
Graham Campbell
80bb601813 Merge pull request #1732 from CachetHQ/database-backups
Database backups
2016-05-06 21:56:26 +01:00
James Brooks
21b60e0708 Take a backup before calling app commands. Closes #1730 2016-05-06 21:11:57 +01:00
Graham Campbell
0fd88a6ba7 Merge pull request #1761 from CachetHQ/cleanup-subscribers-component-deletions
When removing a component, clean up the subscriptions
2016-05-06 14:12:39 +01:00
Graham Campbell
8da7bde2a5 Merge pull request #1760 from CachetHQ/update-deps
Update dependencies
2016-05-06 14:11:59 +01:00
James Brooks
ceb119b226 When removing a component, clean up the subscriptions. Closes #1739 2016-05-06 13:54:00 +01:00
James Brooks
6790d85a87 Update dependencies 2016-05-06 13:46:46 +01:00
Graham Campbell
873f61fde6 Merge pull request #1759 from CachetHQ/powered-by-change
Change the powered by text
2016-05-06 13:00:54 +01:00
James Brooks
f716c7fd1d Change the powered by text. Closes #1758 2016-05-06 12:50:30 +01:00
James Brooks
50ac69b234 Merge pull request #1757 from CachetHQ/fix-pgsql-metrics
Fixes metrics repository in PGSQL
2016-05-05 18:13:59 +01:00
James Brooks
712b1078f2 Fixes metrics repository in PGSQL. Closes #1741 2016-05-05 17:59:30 +01:00
James Brooks
893e61e319 Merge pull request #1756 from CachetHQ/command-bus-db-transactions
Command bus DB transactions
2016-05-05 17:47:59 +01:00
James Brooks
bfd0ccd652 Commands now use db transactions. Fixes #1745 2016-05-05 16:47:41 +01:00
James Brooks
3feb93c074 Remove dump and die call 2016-05-05 16:46:37 +01:00
James Brooks
a5ce958b92 Merge pull request #1753 from CachetHQ/configure-automatic-localization
Allow configuring of automatic localization
2016-05-05 11:22:24 +01:00
James Brooks
47e1dff5c5 Allow configuring of automatic localization. Closes #1747 2016-05-05 10:50:14 +01:00
James Brooks
9ddf9e3e1a Merge pull request #1751 from CachetHQ/fix-component-group-api
Fix component group api
2016-05-05 10:49:44 +01:00
James Brooks
9747cb9204 Added missing docblock 2016-05-05 10:24:01 +01:00
James Brooks
bd35db8d87 Default collapsed value to 0 (always open) 2016-05-05 10:23:18 +01:00
James Brooks
23ff795809 Don't pass through default order value when updating group 2016-05-05 10:23:02 +01:00
Graham Campbell
48d2721605 Updated dependencies 2016-05-04 21:52:01 +01:00
Graham Campbell
9d78092121 Merge pull request #1750 from CachetHQ/fix-str
Fixed a bad import
2016-05-04 20:08:21 +01:00
Graham Campbell
a0b1501b2a Fixed a bad import 2016-05-04 16:38:09 +01:00
Graham Campbell
c4e724e0e6 Rebuilt assets 2016-05-01 16:24:16 +01:00
James Brooks
792a8f6a07 Bower has changed the rules in which a name must follow 2016-05-01 16:06:24 +01:00
Graham Campbell
684b480338 Merge pull request #1738 from CachetHQ/fix-subscribers
Fix subscribers
2016-05-01 16:05:41 +01:00
Joe Cohen
f1b61c7ef1 Applied fixes from StyleCI 2016-05-01 16:04:43 +01:00
Joseph Cohen
05bb91d2d9 Global subscribers and fix notifications 2016-05-01 16:04:43 +01:00
Joseph Cohen
5abd25c408 Redirect subscribers to manage 2016-05-01 16:04:43 +01:00
Graham Campbell
e1a8e2220d Merge branch '2.2' into 2.3
# Conflicts:
#	VERSION
#	public/build/dist/js/all-a62567215d.js
#	public/build/rev-manifest.json
2016-04-29 17:05:53 +01:00
Graham Campbell
9d9d3b2e6c Bumped version 2016-04-29 16:58:35 +01:00
Graham Campbell
df373e93ef Merge branch '2.2' into 2.3
# Conflicts:
#	composer.lock
2016-04-27 14:58:20 +01:00
Graham Campbell
28e7af7107 Merge pull request #1582 from CachetHQ/metric-point-enhancement
Refactoring metric point storage
2016-04-25 20:42:11 +01:00
James Brooks
f9bc46b460 Refactored the way we store metrics 2016-04-25 20:42:00 +01:00
James Brooks
3730ca8811 Rebuild assets 2016-04-25 16:03:48 +01:00
James Brooks
69716730ac Update dependencies 2016-04-25 16:03:40 +01:00
Graham Campbell
28bf01396c Merge branch '2.2'
# Conflicts:
#	VERSION
2016-04-23 11:04:10 +01:00
Graham Campbell
a9185b7876 Bumped version 2016-04-23 11:00:30 +01:00
414 changed files with 8557 additions and 4703 deletions

View File

@@ -1,4 +0,0 @@
{
"directory": "vendor/bower_components",
"interactive": false
}

View File

@@ -1,16 +1,8 @@
Before submitting your issue, please make sure that you've checked all of the checkboxes below.
Before submitting your issue, please make sure that you've checked the checkboxes below.
- [ ] You're running the [latest release](https://github.com/CachetHQ/Cachet/releases/latest) version of Cachet.
- [ ] Ensure that you're running at least PHP 5.5.9, you can check this by running `php -v`
- [ ] You've ran `rm -rf bootstrap/cache/*` from the root of your Cachet installation.
To help us better understand your issue, please answer the following — cheers!
### Your setup
- *What version of Cachet?*
- *What database driver? MySQL? Postgres? SQLite?*
- *What version of PHP?*
- [ ] I am running the [latest release](https://github.com/CachetHQ/Cachet/releases/latest) version of Cachet.
- [ ] I am running at least PHP 5.5.9. *You can check this by running `php -v`.*
- [ ] I have ran `rm -rf bootstrap/cache/*`.
### Expected behaviour
@@ -23,3 +15,6 @@ To help us better understand your issue, please answer the following — cheers!
### Steps to reproduce
*If your issue requires any specific steps to reproduce, please outline them here.*
1. First step
2. Second step

View File

@@ -1,21 +1,19 @@
language: php
sudo: false
dist: trusty
php:
- 5.5.9
- 5.5
- 5.6
- 7.0
- 7.1
- hhvm
sudo: false
before_install: cp .env.example .env
install:
- travis_retry composer install --no-interaction --no-scripts --prefer-source
install: travis_retry composer install --no-interaction --no-scripts --prefer-source
script:
- if [ "$TRAVIS_PHP_VERSION" != "5.6" ] || [ "$TRAVIS_PULL_REQUEST" != false ]; then vendor/bin/phpunit; fi
- if [ "$TRAVIS_PHP_VERSION" == "5.6" ] && [ "$TRAVIS_PULL_REQUEST" == false ]; then vendor/bin/phpunit --coverage-clover build/logs/clover.xml; fi
after_script:
- if [ "$TRAVIS_PHP_VERSION" == "5.6" ] && [ "$TRAVIS_PULL_REQUEST" == false ]; then wget https://scrutinizer-ci.com/ocular.phar; fi
- if [ "$TRAVIS_PHP_VERSION" == "5.6" ] && [ "$TRAVIS_PULL_REQUEST" == false ]; then php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml; fi
script: vendor/bin/phpunit

View File

@@ -1,15 +1,14 @@
# Cachet
[![StyleCI](https://styleci.io/repos/26730195/shield)](https://styleci.io/repos/26730195/)
[![Build Status](https://img.shields.io/travis/CachetHQ/Cachet/master.svg?style=flat-square)](https://travis-ci.org/CachetHQ/Cachet)
[![Quality Score](https://img.shields.io/scrutinizer/g/CachetHQ/Cachet.svg?style=flat-square)](https://scrutinizer-ci.com/g/CachetHQ/Cachet)
[![Build Status](https://img.shields.io/travis/CachetHQ/Cachet/2.3.svg?style=flat-square)](https://travis-ci.org/CachetHQ/Cachet)
[![Software License](https://img.shields.io/badge/license-BSD3-brightgreen.svg?style=flat-square)](LICENSE)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/cachet/localized.png)](http://translate.cachethq.io/project/cachet)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/cachet/localized.svg)](http://translate.cachethq.io/project/cachet)
[![Packagist](https://img.shields.io/packagist/v/cachethq/cachet.svg?style=flat-square)](https://packagist.org/packages/cachethq/cachet)
![Screenshot](https://cachethq.io/img/main-interface.jpg)
Cachet is a beautiful and powerful open source status page system, a free replacement to services such as StatusPage.io, Status.io and others.
Cachet is a beautiful and powerful open source status page system.
## Features
@@ -22,11 +21,7 @@ Cachet is a beautiful and powerful open source status page system, a free replac
- Metrics
- Cross-database support: MySQL, PostgreSQL and SQLite
- Subscriber notifications via Email
- Two factor authentication, with Google Authenticator
## Usage in production
The `master` branch of this repository is a development branch and **should not** be used in production. Instead, please check out the latest tag release.
- Two factor authentication using Google Authenticator
## Requirements
@@ -34,15 +29,15 @@ The `master` branch of this repository is a development branch and **should not*
- Apache or Nginx server
- [Composer](https://getcomposer.org)
## How to contribute
## I'm looking to contribute to this awesome project!
We're always looking for contributions that improve Cachet. It's easy to get started and you don't even need to know how to write a single line of code!
Sweet, we're always looking for contributions that improve Cachet! It's easy to get started and you don't even need to know how to write a single line of code!
### Contributing as a non-developer/non-designer
If you're one of the more linguistically talented people in the world who can speak and write more than just English, we're always looking for new [translations](#translations).
We're always looking for new [translations](#translations).
Of course bug reports, feature requests and [documentation](https://docs.cachethq.io) are always being sought after.
Of course bug reports, feature requests and [documentation](https://docs.cachethq.io) are always appreciated.
### Contributing as a designer
@@ -52,65 +47,60 @@ You'll need to install Node.js, Bower and Gulp.
To get started you can do the following:
1. Install Node.js, Bower and Gulp.
2. Install the assets; `bower install`
3. Modify the SCSS files in `./resources/assets/sass/`
4. Run `gulp`
1. Install Node.js and our dev dependencies.
2. Make your changes to the SCSS files in `./resources/assets/sass/`
3. Run `gulp`
If you're making a lot of changes, you'll find that running `gulp watch` will really help you out!
If you're making a lot of changes, you'll find that running `gulp watch` will make life easier for you!
### Contributing as a developer
Built using [Laravel](https://laravel.com), Cachet is very easy to jump into. Have a look around you'll find it surprisingly easy!
Built using [Laravel](https://laravel.com).
These extra dependencies are required to develop Cachet:
We use these extra dependencies to develop Cachet:
- Node.js
- Bower
- Gulp
- Git
Once cloned to your local machine, you'll need some demo data! Simply run `php artisan cachet:seed` to get the demo installation on the go.
Once cloned to your local machine, you'll need some demo data! Run `php artisan cachet:seed` to get the demo installation ready for action.
## Installation, Upgrades and Documentation
You can now find our documentation at [https://docs.cachethq.io](https://docs.cachethq.io).
Documentation is found at [https://docs.cachethq.io](https://docs.cachethq.io).
Here are some useful quick links:
- [Installing Cachet](https://docs.cachethq.io/docs/installing-cachet)
- [Getting started with Docker](https://docs.cachethq.io/docs/get-started-with-docker)
- [Getting started with Vagrant](https://docs.cachethq.io/docs/get-started-with-vagrant)
### Demo Account
To test out the demo, you may login to the [Dashboard](https://demo.cachethq.io/dashboard) with the following:
- **Username:** test or test@test.com
- **Password:** test123
- **Username:** `test` or `test@test.com`
- **Password:** `test123`
The demo is reset every half hour.
### Release Notes
All releases are listed on the [Releases page](https://github.com/CachetHQ/Cachet/releases) of the [Cachet GitHub repository](https://github.com/CachetHQ/Cachet). On the Releases page, you can also find the release notes for each release.
We list releases on the [Releases page](https://github.com/CachetHQ/Cachet/releases) of the [Cachet GitHub repository](https://github.com/CachetHQ/Cachet). On the Releases page, you can also find the release notes for each release.
## Translations
A special thank you to our [translators](https://crowdin.com/project/cachet/activity_stream), who have allowed us to share Cachet with the world. If you'd like to contribute translations, please check out our [CrowdIn project](https://crowdin.com/project/cachet).
If you'd like to contribute translations, please check out our [CrowdIn project](https://crowdin.com/project/cachet).
> Thank you to our [translators](https://crowdin.com/project/cachet/activity_stream), who have allowed us to share Cachet with the world!
## Show your support
Cachet is a BSD-3-licensed open source project. If you'd like to support future development, check out the [Patreon campaign](https://patreon.com/jbrooksuk).
## Professional Installation Service
We offer a professional installation service. To find out more, email us at [support@alt-three.com](mailto:support@alt-three.com?Cachet Installation)
## Security Vulnerabilities
If you discover a security vulnerability within Cachet, please send an e-mail to us at support@alt-three.com. All security vulnerabilities will be promptly addressed.
## Installations
We offer a paid installation service, which starts at $99 but is subject to change, dependant on your setup and infrastructure.
To find out more, email us at support@alt-three.com
## Donations
Cachet is open source, we don't have any licensing models and don't run ads on the [website](https://cachethq.io).
If you'd like to donate towards further development of Cachet you can do so via Paypal.
[![Donate](https://www.paypalobjects.com/en_GB/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D4M5LVULVPPKL)
If you discover a security vulnerability within Cachet, please send an e-mail to us at support@alt-three.com. We handle all security vulnerabilities on a case-by-case basis.

View File

@@ -1 +1 @@
2.2.2
2.3.12

View File

@@ -69,6 +69,20 @@ final class AddMetricCommand
*/
public $default_view;
/**
* The threshold to buffer the metric points in.
*
* @var int
*/
public $threshold;
/**
* The order of which to place the metric in.
*
* @var int
*/
public $order;
/**
* The validation rules.
*
@@ -84,6 +98,8 @@ final class AddMetricCommand
'display_chart' => 'int',
'places' => 'int|between:0,4',
'default_view' => 'int|between:0,3',
'threshold' => 'numeric|between:0,10',
'order' => 'int',
];
/**
@@ -97,10 +113,12 @@ final class AddMetricCommand
* @param int $display_chart
* @param int $places
* @param int $default_view
* @param int $threshold
* @param int $order
*
* @return void
*/
public function __construct($name, $suffix, $description, $default_value, $calc_type, $display_chart, $places, $default_view)
public function __construct($name, $suffix, $description, $default_value, $calc_type, $display_chart, $places, $default_view, $threshold, $order = 0)
{
$this->name = $name;
$this->suffix = $suffix;
@@ -110,5 +128,7 @@ final class AddMetricCommand
$this->display_chart = $display_chart;
$this->places = $places;
$this->default_view = $default_view;
$this->threshold = $threshold;
$this->order = $order;
}
}

View File

@@ -42,7 +42,7 @@ final class AddMetricPointCommand
* @var string[]
*/
public $rules = [
'value' => 'int',
'value' => 'numeric',
'created_at' => 'string',
];

View File

@@ -78,6 +78,20 @@ final class UpdateMetricCommand
*/
public $default_view;
/**
* The threshold to buffer the metric points in.
*
* @var int
*/
public $threshold;
/**
* The order of which to place the metric in.
*
* @var int|null
*/
public $order;
/**
* The validation rules.
*
@@ -93,6 +107,8 @@ final class UpdateMetricCommand
'display_chart' => 'int',
'places' => 'numeric|between:0,4',
'default_view' => 'numeric|between:0,4',
'threshold' => 'numeric|between:0,10',
'order' => 'int',
];
/**
@@ -107,10 +123,12 @@ final class UpdateMetricCommand
* @param int $display_chart
* @param int $places
* @param int $default_view
* @param int $threshold
* @param int|null $order
*
* @return void
*/
public function __construct(Metric $metric, $name, $suffix, $description, $default_value, $calc_type, $display_chart, $places, $default_view)
public function __construct(Metric $metric, $name, $suffix, $description, $default_value, $calc_type, $display_chart, $places, $default_view, $threshold, $order = null)
{
$this->metric = $metric;
$this->name = $name;
@@ -121,5 +139,7 @@ final class UpdateMetricCommand
$this->display_chart = $display_chart;
$this->places = $places;
$this->default_view = $default_view;
$this->threshold = $threshold;
$this->order = $order;
}
}

View File

@@ -14,6 +14,11 @@ namespace CachetHQ\Cachet\Bus\Commands\Metric;
use CachetHQ\Cachet\Models\Metric;
use CachetHQ\Cachet\Models\MetricPoint;
/**
* This is the update metric point command.
*
* @author James Brooks <james@alt-three.com>
*/
final class UpdateMetricPointCommand
{
/**
@@ -33,7 +38,7 @@ final class UpdateMetricPointCommand
/**
* The metric point value.
*
* @var int
* @var float
*/
public $value;
@@ -50,7 +55,7 @@ final class UpdateMetricPointCommand
* @var string[]
*/
public $rules = [
'value' => 'int',
'value' => 'numeric',
'created_at' => 'string',
];
@@ -59,7 +64,7 @@ final class UpdateMetricPointCommand
*
* @param \CachetHQ\Cachet\Models\MetricPoint $point
* @param \CachetHQ\Cachet\Models\Metric $metric
* @param int $value
* @param float $value
* @param string $created_at
*
* @return void

View File

@@ -33,7 +33,7 @@ final class SubscribeSubscriberCommand
public $verified;
/**
* The subscriptions that we want to add.
* The list of subscriptions to set the subscriber up with.
*
* @var array|null
*/
@@ -53,7 +53,7 @@ final class SubscribeSubscriberCommand
*
* @param string $email
* @param bool $verified
* @param null|array $subscriptions
* @param array|null $subscriptions
*
* @return void
*/

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\Subscriber;
use CachetHQ\Cachet\Models\Subscriber;
/**
* This is the subscribe subscriber command.
*
* @author Joseph Cohen <joe@alt-three.com>
*/
final class UpdateSubscriberSubscriptionCommand
{
/**
* The subscriber email.
*
* @var \CachetHQ\Cachet\Models\Subscriber
*/
public $subscriber;
/**
* The subscriptions that we want to add.
*
* @var array|null
*/
public $subscriptions;
/**
* Create a new subscribe subscriber command instance.
*
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
* @param null|array $subscriptions
*
* @return void
*/
public function __construct($subscriber, $subscriptions = null)
{
$this->subscriber = $subscriber;
$this->subscriptions = $subscriptions;
}
}

View File

@@ -1,26 +0,0 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Exceptions\Subscriber;
use CachetHQ\Cachet\Bus\Exceptions\ExceptionInterface;
use Exception;
/**
* This is the already subscribed exception class.
*
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
*/
class AlreadySubscribedException extends Exception implements ExceptionInterface
{
//
}

View File

@@ -35,6 +35,8 @@ class AddMetricCommandHandler
'display_chart' => $command->display_chart,
'places' => $command->places,
'default_view' => $command->default_view,
'threshold' => $command->threshold,
'order' => $command->order,
]);
event(new MetricWasAddedEvent($metric));

View File

@@ -15,6 +15,7 @@ use CachetHQ\Cachet\Bus\Commands\Metric\AddMetricPointCommand;
use CachetHQ\Cachet\Bus\Events\Metric\MetricPointWasAddedEvent;
use CachetHQ\Cachet\Dates\DateFactory;
use CachetHQ\Cachet\Models\MetricPoint;
use Carbon\Carbon;
class AddMetricPointCommandHandler
{
@@ -49,19 +50,35 @@ class AddMetricPointCommandHandler
$metric = $command->metric;
$createdAt = $command->created_at;
$data = [
'metric_id' => $metric->id,
'value' => $command->value,
];
// Do we have an existing point with the same value?
$point = $this->findOrCreatePoint($command);
if ($createdAt) {
$data['created_at'] = $this->dates->create('U', $createdAt)->format('Y-m-d H:i:s');
$point->increment('counter', 1);
event(new MetricPointWasAddedEvent($point));
return $point;
}
protected function findOrCreatePoint(AddMetricPointCommand $command)
{
$buffer = Carbon::now()->subMinutes($command->metric->threshold);
$point = MetricPoint::where('metric_id', $command->metric->id)->where('value', $command->value)->where('created_at', '>=', $buffer)->first();
if ($point) {
return $point;
}
$metricPoint = MetricPoint::create($data);
$data = [
'metric_id' => $command->metric->id,
'value' => $command->value,
'counter' => 0,
];
event(new MetricPointWasAddedEvent($metricPoint));
if ($command->created_at) {
$data['created_at'] = $this->dates->create('U', $command->created_at)->format('Y-m-d H:i:s');
}
return $metricPoint;
return MetricPoint::create($data);
}
}

View File

@@ -53,6 +53,8 @@ class UpdateMetricCommandHandler
'display_chart' => $command->display_chart,
'places' => $command->places,
'default_view' => $command->default_view,
'threshold' => $command->threshold,
'order' => $command->order,
];
return array_filter($params, function ($val) {

View File

@@ -51,7 +51,7 @@ class UpdateMetricPointCommandHandler
$data = [
'metric_id' => $metric->id,
'value' => $command->value,
'value' => (float) $command->value,
];
if ($createdAt) {

View File

@@ -14,8 +14,7 @@ namespace CachetHQ\Cachet\Bus\Handlers\Commands\Subscriber;
use CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand;
use CachetHQ\Cachet\Bus\Commands\Subscriber\VerifySubscriberCommand;
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasSubscribedEvent;
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUpdatedSubscriptionsEvent;
use CachetHQ\Cachet\Bus\Exceptions\Subscriber\AlreadySubscribedException;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Subscriber;
use CachetHQ\Cachet\Models\Subscription;
@@ -23,6 +22,8 @@ use CachetHQ\Cachet\Models\Subscription;
* This is the subscribe subscriber command handler.
*
* @author James Brooks <james@alt-three.com>
* @author Joe Cohen <joe@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class SubscribeSubscriberCommandHandler
{
@@ -31,37 +32,38 @@ class SubscribeSubscriberCommandHandler
*
* @param \CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand $command
*
* @throws \CachetHQ\Cachet\Exceptions\AlreadySubscribedException
*
* @return \CachetHQ\Cachet\Models\Subscriber
*/
public function handle(SubscribeSubscriberCommand $command)
{
if (Subscriber::where('email', $command->email)->first() && $command->subscriptions === null) {
throw new AlreadySubscribedException("Cannot subscribe {$command->email} because they're already subscribed.");
if ($subscriber = Subscriber::where('email', $command->email)->first()) {
return $subscriber;
}
$subscriber = Subscriber::firstOrCreate(['email' => $command->email]);
// Decide what to subscribe the subscriber to.
if ($subscriptions = $command->subscriptions) {
foreach ($subscriptions as $subscription => $subscriptionValue) {
Subscription::firstOrCreate([
'subscriber_id' => $subscriber->id,
$subscription => $subscriptionValue,
]);
}
$components = Component::whereIn('id', $subscriptions)->get();
} else {
$components = Component::all();
}
if ($subscriber->is_verified === false) {
if ($command->verified) {
dispatch(new VerifySubscriberCommand($subscriber));
} else {
event(new SubscriberHasSubscribedEvent($subscriber));
}
$components->map(function ($component) use ($subscriber) {
Subscription::create([
'subscriber_id' => $subscriber->id,
'component_id' => $component->id,
]);
});
if ($command->verified) {
dispatch(new VerifySubscriberCommand($subscriber));
} else {
event(new SubscriberHasUpdatedSubscriptionsEvent($subscriber));
event(new SubscriberHasSubscribedEvent($subscriber));
}
$subscriber->load('subscriptions');
return $subscriber;
}
}

View File

@@ -13,7 +13,6 @@ namespace CachetHQ\Cachet\Bus\Handlers\Commands\Subscriber;
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriberCommand;
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUnsubscribedEvent;
use CachetHQ\Cachet\Models\Subscriber;
class UnsubscribeSubscriberCommandHandler
{

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\Subscriber;
use CachetHQ\Cachet\Bus\Commands\Subscriber\UpdateSubscriberSubscriptionCommand;
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUpdatedSubscriptionsEvent;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Subscriber;
use CachetHQ\Cachet\Models\Subscription;
/**
* This is the subscribe subscriber command handler.
*
* @author Joseph Cohen <joe@alt-three.com>
*/
class UpdateSubscriberSubscriptionCommandHandler
{
/**
* Handle the subscribe subscriber command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Subscriber\UpdateSubscriberSubscriptionCommand $command
*
* @return \CachetHQ\Cachet\Models\Subscriber
*/
public function handle(UpdateSubscriberSubscriptionCommand $command)
{
$subscriber = $command->subscriber;
$subscriptions = $command->subscriptions ?: [];
$components = Component::all();
$updateSubscriptions = $components->filter(function ($item) use ($subscriptions) {
return in_array($item->id, $subscriptions);
});
$subscriber->global = ($updateSubscriptions->count() === $components->count());
$subscriber->subscriptions()->delete();
if (!$updateSubscriptions->isEmpty()) {
foreach ($updateSubscriptions as $subscription) {
Subscription::firstOrCreate([
'subscriber_id' => $subscriber->id,
'component_id' => $subscription->id,
]);
}
}
$subscriber->save();
event(new SubscriberHasUpdatedSubscriptionsEvent($subscriber));
return $subscriber;
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Events\Component;
use CachetHQ\Cachet\Bus\Events\Component\ComponentWasRemovedEvent;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Subscription;
/**
* This is the cleanup component subscriptions handler.
*
* @author James Brooks <james@alt-three.com>
*/
class CleanupComponentSubscriptionsHandler
{
/**
* Handle the event.
*
* @param \CachetHQ\Cachet\Bus\Events\Component\ComponentWasRemovedEvent $event
*
* @return void
*/
public function handle(ComponentWasRemovedEvent $event)
{
$component = $event->component;
$subscription = Subscription::forComponent($component->id);
// Cleanup the subscriptions.
$subscription->delete();
}
}

View File

@@ -13,7 +13,7 @@ namespace CachetHQ\Cachet\Bus\Handlers\Events\Component;
use CachetHQ\Cachet\Bus\Events\Component\ComponentWasUpdatedEvent;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Subscription;
use CachetHQ\Cachet\Models\Subscriber;
use Illuminate\Contracts\Mail\MailQueue;
use Illuminate\Mail\Message;
use McCool\LaravelAutoPresenter\Facades\AutoPresenter;
@@ -27,16 +27,25 @@ class SendComponentUpdateEmailNotificationHandler
*/
protected $mailer;
/**
* The subscriber instance.
*
* @var \CachetHQ\Cachet\Models\Subscriber
*/
protected $subscriber;
/**
* Create a new send incident email notification handler.
*
* @param \Illuminate\Contracts\Mail\Mailer $mailer
* @param \Illuminate\Contracts\Mail\Mailer $mailer
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
*
* @return void
*/
public function __construct(MailQueue $mailer)
public function __construct(MailQueue $mailer, Subscriber $subscriber)
{
$this->mailer = $mailer;
$this->subscriber = $subscriber;
}
/**
@@ -48,7 +57,42 @@ class SendComponentUpdateEmailNotificationHandler
*/
public function handle(ComponentWasUpdatedEvent $event)
{
$component = AutoPresenter::decorate($event->component);
$component = $event->component;
// First notify all global subscribers.
$globalSubscribers = $this->subscriber->isVerified()->isGlobal()->get();
foreach ($globalSubscribers as $subscriber) {
$this->notify($component, $subscriber);
}
$notified = $globalSubscribers->pluck('id')->all();
// Notify the remaining component specific subscribers.
$componentSubscribers = $this->subscriber
->isVerified()
->forComponent($component->id)
->get()
->reject(function ($subscriber) use ($notified) {
return in_array($subscriber->id, $notified);
});
foreach ($componentSubscribers as $subscriber) {
$this->notify($component, $subscriber);
}
}
/**
* Send notification to subscriber.
*
* @param \CachetHQ\Cachet\Models\Component $component
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
*
* @return \Illuminate\Database\Eloquent\Collection
*/
public function notify(Component $component, Subscriber $subscriber)
{
$component = AutoPresenter::decorate($component);
$mail = [
'subject' => trans('cachet.subscriber.email.component.subject'),
@@ -56,18 +100,14 @@ class SendComponentUpdateEmailNotificationHandler
'component_human_status' => $component->human_status,
];
foreach (Subscription::isVerifiedForComponent($component->id)->with('subscriber')->get() as $subscription) {
$subscriber = $subscription->subscriber;
$mail['email'] = $subscriber->email;
$mail['manage_link'] = route('subscribe.manage', ['code' => $subscriber->verify_code]);
$mail['unsubscribe_link'] = route('subscribe.unsubscribe', ['code' => $subscriber->verify_code, 'subscription' => $subscription->id]);
$mail['email'] = $subscriber->email;
$mail['manage_link'] = route('subscribe.manage', ['code' => $subscriber->verify_code]);
$this->mailer->queue([
'html' => 'emails.components.update-html',
'text' => 'emails.components.update-text',
], $mail, function (Message $message) use ($mail) {
$message->to($mail['email'])->subject($mail['subject']);
});
}
$this->mailer->queue([
'html' => 'emails.components.update-html',
'text' => 'emails.components.update-text',
], $mail, function (Message $message) use ($mail) {
$message->to($mail['email'])->subject($mail['subject']);
});
}
}

View File

@@ -50,7 +50,7 @@ class SendIncidentEmailNotificationHandler
/**
* Handle the event.
*
* @param \CachetHQ\Cachet\Bus\Events\Incident\IncidentHasReportedEvent $event
* @param \CachetHQ\Cachet\Bus\Events\Incident\IncidentWasReportedEvent $event
*
* @return void
*/
@@ -60,32 +60,74 @@ class SendIncidentEmailNotificationHandler
return false;
}
// Only send emails for public incidents.
if ($event->incident->visible === 0) {
return;
}
// First notify all global subscribers.
$globalSubscribers = $this->subscriber->isVerified()->isGlobal()->get();
foreach ($globalSubscribers as $subscriber) {
$this->notify($event, $subscriber);
}
if (!$event->incident->component) {
return;
}
$notified = $globalSubscribers->pluck('id')->all();
// Notify the remaining component specific subscribers.
$componentSubscribers = $this->subscriber
->isVerified()
->forComponent($event->incident->component->id)
->get()
->reject(function ($subscriber) use ($notified) {
return in_array($subscriber->id, $notified);
});
foreach ($componentSubscribers as $subscriber) {
$this->notify($event, $subscriber);
}
}
/**
* Send notification to subscriber.
*
* @param \CachetHQ\Cachet\Bus\Events\IncidentWasReportedEvent $event
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
*
* @return \Illuminate\Database\Eloquent\Collection
*/
public function notify(IncidentWasReportedEvent $event, $subscriber)
{
$incident = AutoPresenter::decorate($event->incident);
$component = AutoPresenter::decorate($event->incident->component);
// Only send emails for public incidents.
if ($event->incident->visible === 1) {
foreach ($this->subscriber->isVerified()->get() as $subscriber) {
$mail = [
'email' => $subscriber->email,
'subject' => 'New incident reported.',
'has_component' => ($event->incident->component) ? true : false,
'component_name' => $component ? $component->name : null,
'status' => $incident->human_status,
'html_content' => $incident->formattedMessage,
'text_content' => $incident->message,
'token' => $subscriber->token,
'manage_link' => route('subscribe.manage', ['code' => $subscriber->verify_code]),
'unsubscribe_link' => route('subscribe.unsubscribe', ['code' => $subscriber->verify_code]),
];
$mail = [
'email' => $subscriber->email,
'subject' => trans('cachet.subscriber.email.incident.subject', [
'status' => $incident->human_status,
'name' => $incident->name,
]),
'has_component' => ($event->incident->component) ? true : false,
'component_name' => $component ? $component->name : null,
'name' => $incident->name,
'timestamp' => $incident->created_at_formatted,
'status' => $incident->human_status,
'html_content' => $incident->formattedMessage,
'text_content' => $incident->message,
'token' => $subscriber->token,
'manage_link' => route('subscribe.manage', ['code' => $subscriber->verify_code]),
'unsubscribe_link' => route('subscribe.unsubscribe', ['code' => $subscriber->verify_code]),
];
$this->mailer->queue([
'html' => 'emails.incidents.new-html',
'text' => 'emails.incidents.new-text',
], $mail, function (Message $message) use ($mail) {
$message->to($mail['email'])->subject($mail['subject']);
});
}
}
$this->mailer->queue([
'html' => 'emails.incidents.new-html',
'text' => 'emails.incidents.new-text',
], $mail, function (Message $message) use ($mail) {
$message->to($mail['email'])->subject($mail['subject']);
});
}
}

View File

@@ -50,7 +50,7 @@ class SendMaintenanceEmailNotificationHandler
/**
* Handle the event.
*
* @param \CachetHQ\Cachet\Bus\Events\MaintenanceHasScheduledEvent $event
* @param \CachetHQ\Cachet\Bus\Events\MaintenanceWasScheduledEvent $event
*
* @return void
*/
@@ -60,27 +60,73 @@ class SendMaintenanceEmailNotificationHandler
return false;
}
$data = AutoPresenter::decorate($event->incident);
// Only send emails for public incidents.
if ($event->incident->visible === 0) {
return;
}
foreach ($this->subscriber->isVerified()->get() as $subscriber) {
$mail = [
'email' => $subscriber->email,
'subject' => 'Scheduled maintenance.',
'status' => $data->human_status,
'html_content' => $data->formattedMessage,
'text_content' => $data->message,
'scheduled_at' => $data->scheduled_at_formatted,
'token' => $subscriber->token,
'manage_link' => route('subscribe.manage', ['code' => $subscriber->verify_code]),
'unsubscribe_link' => route('subscribe.unsubscribe', ['code' => $subscriber->verify_code]),
];
// First notify all global subscribers.
$globalSubscribers = $this->subscriber->isVerified()->isGlobal()->get();
$this->mailer->queue([
'html' => 'emails.incidents.maintenance-html',
'text' => 'emails.incidents.maintenance-text',
], $mail, function (Message $message) use ($mail) {
$message->to($mail['email'])->subject($mail['subject']);
foreach ($globalSubscribers as $subscriber) {
$this->notify($event, $subscriber);
}
if (!$event->incident->component) {
return;
}
$notified = $globalSubscribers->pluck('id')->all();
// Notify the remaining component specific subscribers.
$componentSubscribers = $this->subscriber
->isVerified()
->forComponent($event->incident->component->id)
->get()
->reject(function ($subscriber) use ($notified) {
return in_array($subscriber->id, $notified);
});
foreach ($componentSubscribers as $subscriber) {
$this->notify($event, $subscriber);
}
}
/**
* Send notification to subscriber.
*
* @param \CachetHQ\Cachet\Bus\Events\MaintenanceWasScheduledEvent $event
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
*
* @return \Illuminate\Database\Eloquent\Collection
*/
public function notify(MaintenanceWasScheduledEvent $event, $subscriber)
{
$incident = AutoPresenter::decorate($event->incident);
$component = AutoPresenter::decorate($event->incident->component);
$mail = [
'email' => $subscriber->email,
'subject' => trans('cachet.subscriber.email.maintenance.subject', [
'name' => $incident->name,
]),
'has_component' => ($event->incident->component) ? true : false,
'component_name' => $component ? $component->name : null,
'name' => $incident->name,
'timestamp' => $incident->scheduled_at_formatted,
'status' => $incident->human_status,
'html_content' => $incident->formattedMessage,
'text_content' => $incident->message,
'token' => $subscriber->token,
'manage_link' => route('subscribe.manage', ['code' => $subscriber->verify_code]),
'unsubscribe_link' => route('subscribe.unsubscribe', ['code' => $subscriber->verify_code]),
];
$this->mailer->queue([
'html' => 'emails.incidents.maintenance-html',
'text' => 'emails.incidents.maintenance-text',
], $mail, function (Message $message) use ($mail) {
$message->to($mail['email'])->subject($mail['subject']);
});
}
}

View File

@@ -46,9 +46,9 @@ class SendSubscriberVerificationEmailHandler
public function handle(SubscriberHasSubscribedEvent $event)
{
$mail = [
'email' => $event->subscriber->email,
'subject' => 'Confirm your subscription.',
'link' => route('subscribe.verify', ['code' => $event->subscriber->verify_code]),
'email' => $event->subscriber->email,
'subject' => 'Confirm your subscription.',
'link' => route('subscribe.verify', ['code' => $event->subscriber->verify_code]),
];
$this->mailer->queue([

View File

@@ -46,9 +46,9 @@ class SendInviteUserEmailHandler
public function handle(UserWasInvitedEvent $event)
{
$mail = [
'email' => $event->invite->email,
'subject' => 'You have been invited.',
'link' => route('signup.invite', ['code' => $event->invite->code]),
'email' => $event->invite->email,
'subject' => 'You have been invited.',
'link' => route('signup.invite', ['code' => $event->invite->code]),
];
$this->mailer->queue([

View File

@@ -0,0 +1,38 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Middleware;
use Closure;
use Illuminate\Support\Facades\DB;
/**
* This is the use database transactions bus middleware class.
*
* @author James Brooks <james@alt-three.com>
*/
class UseDatabaseTransactions
{
/**
* Handle the current command in the pipeline.
*
* @param mixed $command
* @param \Closure $next
*
* @return bool
*/
public function handle($command, Closure $next)
{
return DB::transaction(function () use ($command, $next) {
return $next($command);
});
}
}

View File

@@ -41,16 +41,10 @@ class AppComposer
$view->withAppUrl(Config::get('app.url'));
$view->withAppHeader(Config::get('setting.header'));
$view->withAppFooter(Config::get('setting.footer'));
$view->withAppName($name = Config::get('setting.app_name'));
$view->withAppName(Config::get('setting.app_name'));
$view->withShowSupport($support = Config::get('setting.show_support'));
if ($support) {
$view->withSiteTitle(Config::get('setting.app_name').' | Cachet');
} else {
$view->withSiteTitle(Config::get('setting.app_name'));
}
$view->withAutomaticLocalization(Config::get('setting.automatic_localization'));
$view->withSiteTitle(Config::get('setting.app_name'));
$view->withFontSubset(Config::get('langs.'.Config::get('app.locale').'.subset', 'latin'));
}
}

View File

@@ -28,7 +28,7 @@ class MetricsComposer
{
$metrics = null;
if ($displayMetrics = Config::get('setting.display_graphs')) {
$metrics = Metric::where('display_chart', 1)->orderBy('id')->get();
$metrics = Metric::displayable()->orderBy('order')->orderBy('id')->get();
}
$view->withDisplayMetrics($displayMetrics)

View File

@@ -11,13 +11,38 @@
namespace CachetHQ\Cachet\Composers;
use CachetHQ\Cachet\Integrations\Core\System;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use CachetHQ\Cachet\Models\Incident;
use Illuminate\Contracts\View\View;
/**
* This is the status page composer.
*
* @author James Brooks <james@alt-three.com>
*/
class StatusPageComposer
{
/**
* The system instance.
*
* @var \CachetHQ\Cachet\Integrations\Contracts\System
*/
protected $system;
/**
* Create a new status page composer instance.
*
* @param \CachetHQ\Cachet\Integrations\Contracts\System $system
*
* @return void
*/
public function __construct(System $system)
{
$this->system = $system;
}
/**
* Index page view composer.
*
@@ -27,40 +52,7 @@ class StatusPageComposer
*/
public function compose(View $view)
{
$totalComponents = Component::enabled()->count();
$majorOutages = Component::enabled()->status(4)->count();
$isMajorOutage = $totalComponents ? ($majorOutages / $totalComponents) >= 0.5 : false;
// Default data
$withData = [
'system_status' => 'info',
'system_message' => trans_choice('cachet.service.bad', $totalComponents),
'favicon' => 'favicon-high-alert',
];
if ($isMajorOutage) {
$withData = [
'system_status' => 'danger',
'system_message' => trans_choice('cachet.service.major', $totalComponents),
'favicon' => 'favicon-high-alert',
];
} elseif (Component::enabled()->notStatus(1)->count() === 0) {
// If all our components are ok, do we have any non-fixed incidents?
$incidents = Incident::notScheduled()->orderBy('created_at', 'desc')->get();
$incidentCount = $incidents->count();
if ($incidentCount === 0 || ($incidentCount >= 1 && (int) $incidents->first()->status === 4)) {
$withData = [
'system_status' => 'success',
'system_message' => trans_choice('cachet.service.good', $totalComponents),
'favicon' => 'favicon',
];
}
} else {
if (Component::enabled()->whereIn('status', [2, 3])->count() > 0) {
$withData['favicon'] = 'favicon-medium-alert';
}
}
$status = $this->system->getStatus();
// Scheduled maintenance code.
$scheduledMaintenance = Incident::scheduled()->orderBy('scheduled_at')->get();
@@ -70,7 +62,7 @@ class StatusPageComposer
$componentGroups = ComponentGroup::whereIn('id', $usedComponentGroups)->orderBy('order')->get();
$ungroupedComponents = Component::enabled()->where('group_id', 0)->orderBy('order')->orderBy('created_at')->get();
$view->with($withData)
$view->with($status)
->withComponentGroups($componentGroups)
->withUngroupedComponents($ungroupedComponents)
->withScheduledMaintenance($scheduledMaintenance);

View File

@@ -17,9 +17,9 @@ use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\Metric;
use CachetHQ\Cachet\Models\MetricPoint;
use CachetHQ\Cachet\Models\Setting;
use CachetHQ\Cachet\Models\Subscriber;
use CachetHQ\Cachet\Models\User;
use CachetHQ\Cachet\Settings\Repository;
use DateInterval;
use DateTime;
use Illuminate\Console\Command;
@@ -49,6 +49,27 @@ class DemoSeederCommand extends Command
*/
protected $description = 'Seeds Cachet with demo data.';
/**
* The settings repository.
*
* @var \CachetHQ\Cache\Settings\Repository
*/
protected $settings;
/**
* Create a new demo seeder command instance.
*
* @param \CachetHQ\Cache\Settings\Repository $settings
*
* @return void
*/
public function __construct(Repository $settings)
{
parent::__construct();
$this->settings = $settings;
}
/**
* Execute the console command.
*
@@ -142,6 +163,13 @@ class DemoSeederCommand extends Command
'order' => 1,
'group_id' => 2,
'link' => 'https://styleci.io',
], [
'name' => 'Patreon Page',
'description' => 'Support future development of Cachet.',
'status' => 1,
'order' => 0,
'group_id' => 0,
'link' => 'https://patreon.com/jbrooksuk',
],
];
@@ -293,42 +321,45 @@ EINCIDENT;
{
$defaultSettings = [
[
'name' => 'app_name',
'key' => 'app_name',
'value' => 'Cachet Demo',
], [
'name' => 'app_domain',
'key' => 'app_domain',
'value' => 'https://demo.cachethq.io',
], [
'name' => 'show_support',
'key' => 'show_support',
'value' => '1',
], [
'name' => 'app_locale',
'key' => 'app_locale',
'value' => 'en',
], [
'name' => 'app_timezone',
'key' => 'app_timezone',
'value' => 'Europe/London',
], [
'name' => 'app_incident_days',
'key' => 'app_incident_days',
'value' => '7',
], [
'name' => 'app_analytics',
'key' => 'app_analytics',
'value' => 'UA-58442674-3',
], [
'name' => 'app_analytics_gs',
'key' => 'app_analytics_gs',
'value' => 'GSN-712462-P',
], [
'name' => 'display_graphs',
'key' => 'display_graphs',
'value' => '1',
], [
'name' => 'app_about',
'key' => 'app_about',
'value' => 'This is the demo instance of [Cachet](https://cachethq.io?ref=demo). The open source status page system, for everyone. An [Alt Three](https://alt-three.com) product.',
], [
'key' => 'enable_subscribers',
'value' => '0',
],
];
Setting::truncate();
$this->settings->clear();
foreach ($defaultSettings as $setting) {
Setting::create($setting);
$this->settings->set($setting['key'], $setting['value']);
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Foundation\Exceptions\Displayers;
use Exception;
use GrahamCampbell\Exceptions\Displayers\DisplayerInterface;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
class ThrottleDisplayer implements DisplayerInterface
{
/**
* The request instance.
*
* @var \Illuminate\Http\Request
*/
protected $request;
/**
* Create a new redirect displayer instance.
*
* @param \Illuminate\Http\Request $request
*
* @return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Get the error response associated with the given exception.
*
* @param \Exception $exception
* @param string $id
* @param int $code
* @param string[] $headers
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function display(Exception $exception, $id, $code, array $headers)
{
return redirect()->route('auth.login')->withError(trans('forms.login.rate-limit'));
}
/**
* Get the supported content type.
*
* @return string
*/
public function contentType()
{
return 'text/html';
}
/**
* Can we display the exception?
*
* @param \Exception $original
* @param \Exception $transformed
* @param int $code
*
* @return bool
*/
public function canDisplay(Exception $original, Exception $transformed, $code)
{
return $transformed instanceof TooManyRequestsHttpException && $this->request->is('auth*');
}
/**
* Do we provide verbose information about the exception?
*
* @return bool
*/
public function isVerbose()
{
return false;
}
}

View File

@@ -12,10 +12,21 @@
namespace CachetHQ\Cachet\Foundation\Providers;
use AltThree\Bus\Dispatcher;
use CachetHQ\Cachet\Bus\Middleware\UseDatabaseTransactions;
use CachetHQ\Cachet\Dates\DateFactory;
use CachetHQ\Cachet\Integrations\Credits;
use CachetHQ\Cachet\Integrations\Feed;
use CachetHQ\Cachet\Integrations\Releases;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
/**
* This is the app service provider.
*
* @author James Brooks <james@alt-three.com>
* @author Joe Cohen <joe@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class AppServiceProvider extends ServiceProvider
{
/**
@@ -29,6 +40,8 @@ class AppServiceProvider extends ServiceProvider
return Dispatcher::simpleMapping($command, 'CachetHQ\Cachet\Bus', 'CachetHQ\Cachet\Bus\Handlers');
});
$dispatcher->pipeThrough([UseDatabaseTransactions::class]);
Str::macro('canonicalize', function ($url) {
return preg_replace('/([^\/])$/', '$1/', $url);
});
@@ -42,6 +55,9 @@ class AppServiceProvider extends ServiceProvider
public function register()
{
$this->registerDateFactory();
$this->registerCredits();
$this->registerFeed();
$this->registerReleases();
}
/**
@@ -52,10 +68,53 @@ class AppServiceProvider extends ServiceProvider
protected function registerDateFactory()
{
$this->app->singleton(DateFactory::class, function ($app) {
$appTimezone = $app->config->get('app.timezone');
$cacheTimezone = $app->config->get('cachet.timezone');
$appTimezone = $app['config']->get('app.timezone');
$cacheTimezone = $app['config']->get('cachet.timezone');
return new DateFactory($appTimezone, $cacheTimezone);
});
}
/**
* Register the credits class.
*
* @return void
*/
protected function registerCredits()
{
$this->app->singleton(Credits::class, function ($app) {
$cache = $app['cache.store'];
return new Credits($cache);
});
}
/**
* Register the feed class.
*
* @return void
*/
protected function registerFeed()
{
$this->app->singleton(Feed::class, function ($app) {
$cache = $app['cache.store'];
return new Feed($cache);
});
}
/**
* Register the releases class.
*
* @return void
*/
protected function registerReleases()
{
$this->app->singleton(Releases::class, function ($app) {
$cache = $app['cache.store'];
$token = $app['config']->get('services.github.token');
return new Releases($cache, $token);
});
}
}

View File

@@ -34,7 +34,7 @@ class ComposerServiceProvider extends ServiceProvider
$factory->composer('*', CurrentUserComposer::class);
$factory->composer(['index'], MetricsComposer::class);
$factory->composer(['index', 'single-incident', 'subscribe', 'signup'], StatusPageComposer::class);
$factory->composer(['index', 'single-incident', 'subscribe.*', 'signup', 'dashboard.settings.theme'], ThemeComposer::class);
$factory->composer(['index', 'single-incident', 'subscribe.*', 'signup', 'dashboard.settings.theme', 'emails.*'], ThemeComposer::class);
$factory->composer('dashboard.*', DashboardComposer::class);
$factory->composer(['setup', 'dashboard.settings.localization'], TimezoneLocaleComposer::class);
}

View File

@@ -11,11 +11,20 @@
namespace CachetHQ\Cachet\Foundation\Providers;
use CachetHQ\Cachet\Config\Repository;
use CachetHQ\Cachet\Models\Setting as SettingModel;
use CachetHQ\Cachet\Settings\Cache;
use CachetHQ\Cachet\Settings\Repository;
use Exception;
use Illuminate\Support\ServiceProvider;
use Jenssegers\Date\Date;
/**
* This is the config service provider class.
*
* @author James Brooks <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author Joe Cohen <joe@alt-three.com>
*/
class ConfigServiceProvider extends ServiceProvider
{
/**
@@ -25,8 +34,26 @@ class ConfigServiceProvider extends ServiceProvider
*/
public function boot()
{
$env = $this->app->environment();
$repo = $this->app->make(Repository::class);
$cache = $this->app->make(Cache::class);
$loaded = $cache->load($env);
$this->app->terminating(function () use ($repo, $cache) {
if ($repo->stale()) {
$cache->clear();
}
});
try {
$this->app->config->set('setting', $this->app->setting->all());
if ($loaded === false) {
$loaded = $repo->all();
$cache->store($env, $loaded);
}
$settings = array_merge($this->app->config->get('setting'), $loaded);
$this->app->config->set('setting', $settings);
} catch (Exception $e) {
//
}
@@ -35,9 +62,10 @@ class ConfigServiceProvider extends ServiceProvider
$this->app->config->set('app.url', $appDomain);
}
if ($appLocale = $this->app->config->get('setting.app.locale')) {
if ($appLocale = $this->app->config->get('setting.app_locale')) {
$this->app->config->set('app.locale', $appLocale);
$this->app->translator->setLocale($appLocale);
Date::setLocale($appLocale);
}
if ($appTimezone = $this->app->config->get('setting.app_timezone')) {
@@ -65,10 +93,12 @@ class ConfigServiceProvider extends ServiceProvider
*/
public function register()
{
$this->app->singleton('setting', function () {
return new Repository(new SettingModel());
$this->app->singleton(Cache::class, function ($app) {
return new Cache($app->files, $app->bootstrapPath().'/cachet');
});
$this->app->alias('setting', Repository::class);
$this->app->singleton(Repository::class, function () {
return new Repository(new SettingModel());
});
}
}

View File

@@ -11,11 +11,28 @@
namespace CachetHQ\Cachet\Foundation\Providers;
use CachetHQ\Cachet\GitHub\Release;
use CachetHQ\Cachet\Subscribers\CommandSubscriber;
use Illuminate\Support\ServiceProvider;
class GitHubServiceProvider extends ServiceProvider
/**
* This is the console service provider.
*
* @author James Brooks <james@alt-three.com>
*/
class ConsoleServiceProvider extends ServiceProvider
{
/**
* Boot the service provider.
*
* @return void
*/
public function boot()
{
$subscriber = $this->app->make(CommandSubscriber::class);
$this->app->events->subscribe($subscriber);
}
/**
* Register the service provider.
*
@@ -23,23 +40,6 @@ class GitHubServiceProvider extends ServiceProvider
*/
public function register()
{
$this->registerRelease();
}
/**
* Register the releases class.
*
* @return void
*/
protected function registerRelease()
{
$this->app->singleton('cachet.release', function ($app) {
$cache = $app['cache.store'];
$config = $app['config'];
return new Release($cache, $config);
});
$this->app->alias('cachet.release', Release::class);
//
}
}

View File

@@ -34,14 +34,20 @@ class EventServiceProvider extends ServiceProvider
//
],
'CachetHQ\Cachet\Bus\Events\Component\ComponentWasRemovedEvent' => [
//
'CachetHQ\Cachet\Bus\Handlers\Events\Component\CleanupComponentSubscriptionsHandler',
],
'CachetHQ\Cachet\Bus\Events\Component\ComponentWasUpdatedEvent' => [
'CachetHQ\Cachet\Bus\Handlers\Events\Component\SendComponentUpdateEmailNotificationHandler',
],
'CachetHQ\Cachet\Bus\Events\Incident\IncidentWasRemovedEvent' => [
//
],
'CachetHQ\Cachet\Bus\Events\Incident\IncidentWasReportedEvent' => [
'CachetHQ\Cachet\Bus\Handlers\Events\Incident\SendIncidentEmailNotificationHandler',
],
'CachetHQ\Cachet\Bus\Events\Incident\IncidentWasUpdatedEvent' => [
//
],
'CachetHQ\Cachet\Bus\Events\Incident\MaintenanceWasScheduledEvent' => [
'CachetHQ\Cachet\Bus\Handlers\Events\Incident\SendMaintenanceEmailNotificationHandler',
],

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Foundation\Providers;
use CachetHQ\Cachet\Integrations\Contracts\System as SystemContract;
use CachetHQ\Cachet\Integrations\Core\System;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\ServiceProvider;
/**
* This is the integration service provider.
*
* @author James Brooks <james@alt-three.com>
*/
class IntegrationServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerSystem();
}
/**
* Register the system class.
*
* @return void
*/
protected function registerSystem()
{
$this->app->singleton(SystemContract::class, function (Container $app) {
return new System();
});
}
}

View File

@@ -16,8 +16,15 @@ use CachetHQ\Cachet\Repositories\Metric\MetricRepository;
use CachetHQ\Cachet\Repositories\Metric\MySqlRepository as MetricMySqlRepository;
use CachetHQ\Cachet\Repositories\Metric\PgSqlRepository as MetricPgSqlRepository;
use CachetHQ\Cachet\Repositories\Metric\SqliteRepository as MetricSqliteRepository;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\ServiceProvider;
/**
* This is the repository service provider.
*
* @author James Brooks <james@alt-three.com>
*/
class RepositoryServiceProvider extends ServiceProvider
{
/**
@@ -37,22 +44,21 @@ class RepositoryServiceProvider extends ServiceProvider
*/
protected function registerMetricRepository()
{
$this->app->singleton('cachet.metricrepository', function ($app) {
$dbDriver = $app['config']->get('database.default');
$this->app->singleton(MetricRepository::class, function (Container $app) {
$config = $app->make(ConfigRepository::class);
$driver = $config->get('database.default');
if ($dbDriver == 'mysql') {
$repository = new MetricMySqlRepository();
} elseif ($dbDriver == 'pgsql') {
$repository = new MetricPgSqlRepository();
} elseif ($dbDriver == 'sqlite') {
$repository = new MetricSqliteRepository();
if ($driver == 'mysql') {
$repository = new MetricMySqlRepository($config);
} elseif ($driver == 'pgsql') {
$repository = new MetricPgSqlRepository($config);
} elseif ($driver == 'sqlite') {
$repository = new MetricSqliteRepository($config);
}
$dates = $app->make(DateFactory::class);
return new MetricRepository($repository, $dates);
});
$this->app->alias('cachet.metricrepository', MetricRepository::class);
}
}

View File

@@ -1,70 +0,0 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\GitHub;
use GuzzleHttp\Client;
use Illuminate\Contracts\Cache\Repository as CacheRepository;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
class Release
{
/**
* The cache repository instance.
*
* @var \Illuminate\Contracts\Cache\Repository
*/
protected $cache;
/**
* The config repository instance.
*
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* Creates a new release instance.
*
* @param \Illuminate\Contracts\Cache\Repository $cache
* @param \Illuminate\Contracts\Config\Repository $config
*
* @return void
*/
public function __construct(CacheRepository $cache, ConfigRepository $config)
{
$this->cache = $cache;
$this->config = $config;
}
/**
* Returns the latest GitHub release.
*
* @return string
*/
public function latest()
{
$release = $this->cache->remember('version', 720, function () {
$headers = ['Accept' => 'application/vnd.github.v3+json'];
// We can re-use the Emoji token here, if we have it.
if ($token = $this->config->get('services.github.token')) {
$headers['OAUTH-TOKEN'] = $token;
}
return json_decode((new Client())->get('https://api.github.com/repos/cachethq/cachet/releases/latest', [
'headers' => $headers,
])->getBody(), true);
});
return $release['tag_name'];
}
}

View File

@@ -20,6 +20,13 @@ use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* This is the component group controller.
*
* @author James Brooks <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author Joe Cohen <joe@alt-three.com>
*/
class ComponentGroupController extends AbstractApiController
{
/**
@@ -67,7 +74,7 @@ class ComponentGroupController extends AbstractApiController
$group = dispatch(new AddComponentGroupCommand(
Binput::get('name'),
Binput::get('order', 0),
Binput::get('collapsed')
Binput::get('collapsed', 0)
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
@@ -89,7 +96,7 @@ class ComponentGroupController extends AbstractApiController
$group = dispatch(new UpdateComponentGroupCommand(
$group,
Binput::get('name'),
Binput::get('order', 0),
Binput::get('order'),
Binput::get('collapsed')
));
} catch (QueryException $e) {

View File

@@ -11,6 +11,14 @@
namespace CachetHQ\Cachet\Http\Controllers\Api;
use CachetHQ\Cachet\Integrations\Contracts\System;
use CachetHQ\Cachet\Integrations\Releases;
/**
* This is the general api controller.
*
* @author James Brooks <james@bluebaytravel.co.uk>
*/
class GeneralController extends AbstractApiController
{
/**
@@ -30,6 +38,26 @@ class GeneralController extends AbstractApiController
*/
public function version()
{
return $this->item(CACHET_VERSION);
$latest = app(Releases::class)->latest();
return $this->setMetaData([
'on_latest' => version_compare(CACHET_VERSION, $latest['tag_name']) === 1,
'latest' => $latest,
])->item(CACHET_VERSION);
}
/**
* Get the system status message.
*
* @return \Illuminate\Http\JsonResponse
*/
public function status()
{
$system = app()->make(System::class)->getStatus();
return $this->item([
'status' => $system['system_status'],
'message' => $system['system_message'],
]);
}
}

View File

@@ -84,7 +84,9 @@ class MetricController extends AbstractApiController
Binput::get('calc_type', 0),
Binput::get('display_chart', true),
Binput::get('places', 2),
Binput::get('view', 1)
Binput::get('default_view', Binput::get('view', 1)),
Binput::get('threshold', 5),
Binput::get('order', 0)
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
@@ -112,7 +114,9 @@ class MetricController extends AbstractApiController
Binput::get('calc_type'),
Binput::get('display_chart'),
Binput::get('places'),
Binput::get('view')
Binput::get('default_view', Binput::get('view')),
Binput::get('threshold'),
Binput::get('order')
));
} catch (QueryException $e) {
throw new BadRequestHttpException();

View File

@@ -48,8 +48,8 @@ class MetricPointController extends AbstractApiController
$metricPoint = dispatch(new AddMetricPointCommand(
$metric,
Binput::get('value'),
Binput::get('timestamp'))
);
Binput::get('timestamp')
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}

View File

@@ -21,6 +21,12 @@ use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* This is the subscriber controller class.
*
* @author James Brooks <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class SubscriberController extends AbstractApiController
{
/**
@@ -43,7 +49,11 @@ class SubscriberController extends AbstractApiController
public function postSubscribers()
{
try {
$subscriber = dispatch(new SubscribeSubscriberCommand(Binput::get('email'), Binput::get('verify', false), null));
$subscriber = dispatch(new SubscribeSubscriberCommand(
Binput::get('email'),
Binput::get('verify', false),
Binput::get('components', null)
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}

View File

@@ -41,11 +41,11 @@ class AuthController extends Controller
*/
public function postLogin()
{
$loginData = Binput::only(['login', 'password']);
$loginData = Binput::only(['username', 'password']);
// Login with username or email.
$loginKey = Str::contains($loginData['login'], '@') ? 'email' : 'username';
$loginData[$loginKey] = array_pull($loginData, 'login');
$loginKey = Str::contains($loginData['username'], '@') ? 'email' : 'username';
$loginData[$loginKey] = array_pull($loginData, 'username');
// Validate login credentials.
if (Auth::validate($loginData)) {

View File

@@ -11,34 +11,46 @@
namespace CachetHQ\Cachet\Http\Controllers\Dashboard;
use CachetHQ\Cachet\GitHub\Release;
use CachetHQ\Cachet\Bus\Commands\Component\UpdateComponentCommand;
use CachetHQ\Cachet\Bus\Commands\ComponentGroup\UpdateComponentGroupCommand;
use CachetHQ\Cachet\Http\Controllers\Api\AbstractApiController;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use CachetHQ\Cachet\Models\IncidentTemplate;
use Exception;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Response;
use Illuminate\Database\QueryException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class ApiController extends Controller
class ApiController extends AbstractApiController
{
/**
* Updates a component with the entered info.
*
* @param \CachetHQ\Cachet\Models\Component $component
*
* @throws \Exception
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
*
* @return \CachetHQ\Cachet\Models\Component
*/
public function postUpdateComponent(Component $component)
{
if (!$component->update(Binput::except(['_token']))) {
throw new Exception(trans('dashboard.components.edit.failure'));
try {
dispatch(new UpdateComponentCommand(
$component,
$component->name,
$component->description,
Binput::get('status'),
$component->link,
$component->order,
$component->group_id,
$component->enabled
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $component;
return $this->item($component);
}
/**
@@ -51,11 +63,25 @@ class ApiController extends Controller
$componentData = Binput::get('ids');
foreach ($componentData as $order => $componentId) {
// Ordering should be 1-based, data comes in 0-based
Component::find($componentId)->update(['order' => $order + 1]);
try {
$component = Component::find($componentId);
dispatch(new UpdateComponentCommand(
$component,
$component->name,
$component->description,
$component->status,
$component->link,
$order + 1,
$component->group_id,
$component->enabled
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
}
return $componentData;
return $this->collection(Component::query()->orderBy('order')->get());
}
/**
@@ -68,10 +94,17 @@ class ApiController extends Controller
$groupData = Binput::get('ids');
foreach ($groupData as $order => $groupId) {
ComponentGroup::find($groupId)->update(['order' => $order + 1]);
$group = ComponentGroup::find($groupId);
dispatch(new UpdateComponentGroupCommand(
$group,
$group->name,
$order + 1,
$group->collapsed
));
}
return $groupData;
return $this->collection(ComponentGroup::query()->orderBy('order')->get());
}
/**
@@ -91,20 +124,4 @@ class ApiController extends Controller
throw new ModelNotFoundException("Incident template for $templateSlug could not be found.");
}
/**
* Checks if Cachet is up to date.
*
* @return \Illuminate\Http\JsonResponse
*/
public function checkVersion()
{
$latest = app(Release::class)->latest();
return Response::json([
'cachet_version' => CACHET_VERSION,
'latest_version' => $latest,
'is_latest' => version_compare(CACHET_VERSION, $latest) === 1,
]);
}
}

View File

@@ -11,11 +11,14 @@
namespace CachetHQ\Cachet\Http\Controllers\Dashboard;
use CachetHQ\Cachet\Integrations\Feed;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\Subscriber;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\View;
use Jenssegers\Date\Date;
@@ -36,16 +39,36 @@ class DashboardController extends Controller
protected $timeZone;
/**
* Creates a new dashboard controller.
* The feed integration.
*
* @var \CachetHQ\Cachet\Integrations\Feed
*/
protected $feed;
/**
* Creates a new dashboard controller instance.
*
* @param \CachetHQ\Cachet\Integrations\Feed $feed
*
* @return void
*/
public function __construct()
public function __construct(Feed $feed)
{
$this->feed = $feed;
$this->startDate = new Date();
$this->dateTimeZone = Config::get('cachet.timezone');
}
/**
* Redirect /admin to /dashboard.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function redirectAdmin()
{
return Redirect::route('dashboard.index');
}
/**
* Shows the dashboard view.
*
@@ -56,12 +79,23 @@ class DashboardController extends Controller
$components = Component::orderBy('order')->get();
$incidents = $this->getIncidents();
$subscribers = $this->getSubscribers();
$usedComponentGroups = Component::enabled()->where('group_id', '>', 0)->groupBy('group_id')->pluck('group_id');
$componentGroups = ComponentGroup::whereIn('id', $usedComponentGroups)->orderBy('order')->get();
$ungroupedComponents = Component::enabled()->where('group_id', 0)->orderBy('order')->orderBy('created_at')->get();
$entries = null;
if ($feed = $this->feed->latest()) {
$entries = array_slice($feed->channel->item, 0, 5);
}
return View::make('dashboard.index')
->withPageTitle(trans('dashboard.dashboard'))
->withComponents($components)
->withIncidents($incidents)
->withSubscribers($subscribers);
->withSubscribers($subscribers)
->withEntries($entries)
->withComponentGroups($componentGroups)
->withUngroupedComponents($ungroupedComponents);
}
/**

View File

@@ -31,7 +31,7 @@ class MetricController extends Controller
*/
public function showMetrics()
{
$metrics = Metric::orderBy('created_at', 'desc')->get();
$metrics = Metric::orderBy('order')->orderBy('id')->get();
return View::make('dashboard.metrics.index')
->withPageTitle(trans('dashboard.metrics.metrics').' - '.trans('dashboard.dashboard'))
@@ -79,7 +79,8 @@ class MetricController extends Controller
$metricData['calc_type'],
$metricData['display_chart'],
$metricData['places'],
$metricData['default_view']
$metricData['default_view'],
$metricData['threshold']
));
} catch (ValidationException $e) {
return Redirect::route('dashboard.metrics.add')
@@ -151,7 +152,8 @@ class MetricController extends Controller
Binput::get('calc_type', null, false),
Binput::get('display_chart', null, false),
Binput::get('places', null, false),
Binput::get('default_view', null, false)
Binput::get('default_view', null, false),
Binput::get('threshold', null, false)
));
} catch (ValidationException $e) {
return Redirect::route('dashboard.metrics.edit', ['id' => $metric->id])

View File

@@ -11,7 +11,9 @@
namespace CachetHQ\Cachet\Http\Controllers\Dashboard;
use CachetHQ\Cachet\Integrations\Credits;
use CachetHQ\Cachet\Models\User;
use CachetHQ\Cachet\Settings\Repository;
use Exception;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Routing\Controller;
@@ -19,8 +21,8 @@ use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Str;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Str;
class SettingsController extends Controller
{
@@ -81,6 +83,12 @@ class SettingsController extends Controller
'icon' => 'ion-stats-bars',
'active' => false,
],
'credits' => [
'title' => trans('dashboard.settings.credits.credits'),
'url' => route('dashboard.settings.credits'),
'icon' => 'ion-ios-list',
'active' => false,
],
'about' => [
'title' => CACHET_VERSION,
'url' => 'javascript: void(0);',
@@ -211,6 +219,30 @@ class SettingsController extends Controller
->withSubMenu($this->subMenu);
}
/**
* Show the credits view.
*
* @return \Illuminate\View\View
*/
public function showCreditsView()
{
$this->subMenu['credits']['active'] = true;
$credits = app(Credits::class)->latest();
$backers = $credits['backers'];
$contributors = $credits['contributors'];
shuffle($backers);
shuffle($contributors);
return View::make('dashboard.settings.credits')
->withPageTitle(trans('dashboard.settings.credits.credits').' - '.trans('dashboard.dashboard'))
->withBackers($backers)
->withContributors($contributors)
->withSubMenu($this->subMenu);
}
/**
* Updates the status page settings.
*
@@ -218,9 +250,7 @@ class SettingsController extends Controller
*/
public function postSettings()
{
$redirectUrl = Session::get('redirect_to', route('dashboard.settings.setup'));
$setting = app('setting');
$setting = app(Repository::class);
if (Binput::get('remove_banner') === '1') {
$setting->set('app_banner', null);
@@ -245,29 +275,7 @@ class SettingsController extends Controller
}
if (Binput::hasFile('app_banner')) {
$file = Binput::file('app_banner');
// Image Validation.
// Image size in bytes.
$maxSize = $file->getMaxFilesize();
if ($file->getSize() > $maxSize) {
return Redirect::to($redirectUrl)->withErrors(trans('dashboard.settings.app-setup.too-big', ['size' => $maxSize]));
}
if (!$file->isValid() || $file->getError()) {
return Redirect::to($redirectUrl)->withErrors($file->getErrorMessage());
}
if (!Str::startsWith($file->getMimeType(), 'image/')) {
return Redirect::to($redirectUrl)->withErrors(trans('dashboard.settings.app-setup.images-only'));
}
// Store the banner.
$setting->set('app_banner', base64_encode(file_get_contents($file->getRealPath())));
// Store the banner type.
$setting->set('app_banner_type', $file->getMimeType());
$this->handleUpdateBanner($setting);
}
$excludedParams = [
@@ -287,13 +295,47 @@ class SettingsController extends Controller
$setting->set($settingName, $settingValue);
}
} catch (Exception $e) {
return Redirect::to($redirectUrl)->withErrors(trans('dashboard.settings.edit.failure'));
return Redirect::back()->withErrors(trans('dashboard.settings.edit.failure'));
}
if (Binput::has('app_locale')) {
Lang::setLocale(Binput::get('app_locale'));
}
return Redirect::to($redirectUrl)->withSuccess(trans('dashboard.settings.edit.success'));
return Redirect::back()->withSuccess(trans('dashboard.settings.edit.success'));
}
/**
* Handle updating of the banner image.
*
* @param \CachetHQ\Cachet\Settings\Repository $setting
*
* @return void
*/
protected function handleUpdateBanner(Repository $setting)
{
$file = Binput::file('app_banner');
// Image Validation.
// Image size in bytes.
$maxSize = $file->getMaxFilesize();
if ($file->getSize() > $maxSize) {
return Redirect::to($redirectUrl)->withErrors(trans('dashboard.settings.app-setup.too-big', ['size' => $maxSize]));
}
if (!$file->isValid() || $file->getError()) {
return Redirect::to($redirectUrl)->withErrors($file->getErrorMessage());
}
if (!Str::startsWith($file->getMimeType(), 'image/')) {
return Redirect::to($redirectUrl)->withErrors(trans('dashboard.settings.app-setup.images-only'));
}
// Store the banner.
$setting->set('app_banner', base64_encode(file_get_contents($file->getRealPath())));
// Store the banner type.
$setting->set('app_banner_type', $file->getMimeType());
}
}

View File

@@ -53,7 +53,11 @@ class SubscriberController extends Controller
public function createSubscriberAction()
{
try {
dispatch(new SubscribeSubscriberCommand(Binput::get('email')));
$subscribers = preg_split("/\r\n|\n|\r/", Binput::get('email'));
foreach ($subscribers as $subscriber) {
dispatch(new SubscribeSubscriberCommand($subscriber));
}
} catch (ValidationException $e) {
return Redirect::route('dashboard.subscribers.add')
->withInput(Binput::all())

View File

@@ -12,6 +12,7 @@
namespace CachetHQ\Cachet\Http\Controllers;
use CachetHQ\Cachet\Models\User;
use CachetHQ\Cachet\Settings\Repository;
use Dotenv\Dotenv;
use Dotenv\Exception\InvalidPathException;
use GrahamCampbell\Binput\Facades\Binput;
@@ -41,6 +42,22 @@ class SetupController extends Controller
'redis' => 'Redis',
];
/**
* Array of cache drivers.
*
* @var string[]
*/
protected $mailDrivers = [
'smtp' => 'SMTP',
'mail' => 'Mail',
'sendmail' => 'Sendmail',
'mailgun' => 'Mailgun',
'mandrill' => 'Mandrill',
// 'ses' => 'Amazon SES', this will be available only if aws/aws-sdk-php is installed
'sparkpost' => 'SparkPost',
'log' => 'Log (Testing)',
];
/**
* Array of step1 rules.
*
@@ -72,6 +89,7 @@ class SetupController extends Controller
$this->rulesStep1 = [
'env.cache_driver' => 'required|in:'.implode(',', array_keys($this->cacheDrivers)),
'env.session_driver' => 'required|in:'.implode(',', array_keys($this->cacheDrivers)),
'env.mail_driver' => 'required|in:'.implode(',', array_keys($this->mailDrivers)),
];
$this->rulesStep2 = [
@@ -96,11 +114,6 @@ class SetupController extends Controller
*/
public function getIndex()
{
// If we've copied the .env.example file, then we should try and reset it.
if (strlen(Config::get('app.key')) !== 32) {
$this->keyGenerate();
}
$supportedLanguages = Request::getLanguages();
$userLanguage = Config::get('app.locale');
@@ -116,6 +129,7 @@ class SetupController extends Controller
return View::make('setup')
->withPageTitle(trans('setup.setup'))
->withCacheDrivers($this->cacheDrivers)
->withMailDrivers($this->mailDrivers)
->withUserLanguage($userLanguage)
->withAppUrl(Request::root());
}
@@ -131,6 +145,14 @@ class SetupController extends Controller
$v = Validator::make($postData, $this->rulesStep1);
$v->sometimes('env.mail_host', 'required', function ($input) {
return $input->mail_driver === 'smtp';
});
$v->sometimes(['env.mail_address', 'env.mail_username', 'env.mail_password'], 'required', function ($input) {
return $input->mail_driver !== 'log';
});
if ($v->passes()) {
return Response::json(['status' => 1]);
}
@@ -180,7 +202,7 @@ class SetupController extends Controller
Auth::login($user);
$setting = app('setting');
$setting = app(Repository::class);
$settings = array_pull($postData, 'settings');
@@ -228,33 +250,14 @@ class SetupController extends Controller
try {
(new Dotenv($dir, $file))->load();
$envKey = strtoupper($key);
$envValue = env($envKey) ?: 'null';
file_put_contents($path, str_replace(
env(strtoupper($key)), $value, file_get_contents($path)
$envKey.'='.$envValue, $envKey.'='.$value, file_get_contents($path)
));
} catch (InvalidPathException $e) {
//
}
}
/**
* Generate the app.key value.
*
* @return void
*/
protected function keyGenerate()
{
$key = str_random(32);
$dir = app()->environmentPath();
$file = app()->environmentFile();
$path = "{$dir}/{$file}";
(new Dotenv($dir, $file))->load();
file_put_contents($path, str_replace(
Config::get('app.key'), $key, file_get_contents($path)
));
Config::set('app.key', $key);
}
}

View File

@@ -47,7 +47,7 @@ class SignupController extends Controller
return View::make('signup')
->withCode($invite->code)
->withUsername(Binput::old('username'))
->withEmail(Binput::old('emai', $invite->email));
->withEmail(Binput::old('email', $invite->email));
}
/**

View File

@@ -15,8 +15,9 @@ use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand;
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriberCommand;
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriptionCommand;
use CachetHQ\Cachet\Bus\Commands\Subscriber\UpdateSubscriberSubscriptionCommand;
use CachetHQ\Cachet\Bus\Commands\Subscriber\VerifySubscriberCommand;
use CachetHQ\Cachet\Bus\Exceptions\Subscriber\AlreadySubscribedException;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Subscriber;
use CachetHQ\Cachet\Models\Subscription;
use GrahamCampbell\Binput\Facades\Binput;
@@ -57,20 +58,22 @@ class SubscribeController extends Controller
$subscriptions = Binput::get('subscriptions');
try {
dispatch(new SubscribeSubscriberCommand($email, false, $subscriptions));
} catch (AlreadySubscribedException $e) {
return Redirect::route('subscribe.subscribe')
->withTitle(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
->withErrors(trans('cachet.subscriber.email.already-subscribed', ['email' => $email]));
$verified = false;
$subscription = dispatch(new SubscribeSubscriberCommand($email, $verified));
} catch (ValidationException $e) {
return Redirect::route('subscribe.subscribe')
return Redirect::route('status-page')
->withInput(Binput::all())
->withTitle(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
->withErrors($e->getMessageBag());
}
return Redirect::route('status-page')
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.subscribed')));
$message = $subscription->is_verified ?
trans('cachet.subscriber.email.already-subscribed', ['email' => $email]) :
trans('cachet.subscriber.email.subscribed');
return Redirect::route('subscribe.manage', $subscription->verify_code)
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), $message));
}
/**
@@ -86,16 +89,18 @@ class SubscribeController extends Controller
throw new NotFoundHttpException();
}
$subscriber = Subscriber::where('verify_code', '=', $code)->first();
$subscriber = Subscriber::where('verify_code', $code)->first();
if (!$subscriber || $subscriber->is_verified) {
if (!$subscriber) {
throw new BadRequestHttpException();
}
dispatch(new VerifySubscriberCommand($subscriber));
if (!$subscriber->is_verified) {
dispatch(new VerifySubscriberCommand($subscriber));
}
return Redirect::route('status-page')
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.verified')));
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.verified')));
}
/**
@@ -125,7 +130,7 @@ class SubscribeController extends Controller
}
return Redirect::route('status-page')
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.unsubscribed')));
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.unsubscribed')));
}
/**
@@ -143,10 +148,45 @@ class SubscribeController extends Controller
$subscriber = Subscriber::where('verify_code', '=', $code)->first();
if (!$subscriber || !$subscriber->is_verified) {
if (!$subscriber) {
throw new BadRequestHttpException();
}
return View::make('subscribe.manage')->withSubscriber($subscriber);
return View::make('subscribe.manage')
->withComponents(Component::all())
->withSubscriber($subscriber)
->withSubscriptions($subscriber->subscriptions->pluck('component_id')->all());
}
/**
* Updates the subscription manager for a subscriber.
*
* @param string|null $code
*
* @return \Illuminate\View\View
*/
public function postManage($code = null)
{
if ($code === null) {
throw new NotFoundHttpException();
}
$subscriber = Subscriber::where('verify_code', '=', $code)->first();
if (!$subscriber) {
throw new BadRequestHttpException();
}
try {
dispatch(new UpdateSubscriberSubscriptionCommand($subscriber, Binput::get('subscriptions')));
} catch (ValidationException $e) {
return Redirect::route('subscribe.manage', $subscriber->verify_code)
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
->withErrors($e->getMessageBag());
}
return Redirect::route('subscribe.manage', $subscriber->verify_code)
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.subscribed')));
}
}

View File

@@ -59,6 +59,6 @@ class Kernel extends HttpKernel
'ready' => 'CachetHQ\Cachet\Http\Middleware\ReadyForUse',
'setup' => 'CachetHQ\Cachet\Http\Middleware\SetupAlreadyCompleted',
'subscribers' => 'CachetHQ\Cachet\Http\Middleware\SubscribersConfigured',
'throttle' => 'Illuminate\Routing\Middleware\ThrottleRequests',
'throttle' => 'AltThree\Throttle\ThrottlingMiddleware',
];
}

View File

@@ -55,6 +55,10 @@ class Localize
*/
public function handle(Request $request, Closure $next)
{
if (!(bool) $this->config->get('setting.automatic_localization')) {
return $next($request);
}
$supportedLanguages = $request->getLanguages();
$userLanguage = $this->config->get('app.locale');

View File

@@ -25,7 +25,7 @@ class Timezone
protected $config;
/**
* Creates a new release instance.
* Creates a new timezone middleware instance.
*
* @param \Illuminate\Contracts\Config\Repository $config
*

View File

@@ -33,6 +33,7 @@ class ApiRoutes
$router->group(['middleware' => ['auth.api']], function (Registrar $router) {
$router->get('ping', 'GeneralController@ping');
$router->get('version', 'GeneralController@version');
$router->get('status', 'GeneralController@status');
$router->get('components', 'ComponentController@getComponents');
$router->get('components/groups', 'ComponentGroupController@getGroups');

View File

@@ -29,206 +29,213 @@ class DashboardRoutes
*/
public function map(Registrar $router)
{
$router->group(['middleware' => ['web', 'auth'], 'prefix' => 'dashboard', 'namespace' => 'Dashboard', 'as' => 'dashboard.'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'DashboardController@showDashboard',
]);
$router->group(['middleware' => ['web', 'auth'], 'namespace' => 'Dashboard'], function (Registrar $router) {
$router->get('admin', 'DashboardController@redirectAdmin');
$router->group(['as' => 'components.', 'prefix' => 'components'], function (Registrar $router) {
$router->group(['prefix' => 'dashboard', 'as' => 'dashboard.'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'ComponentController@showComponents',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'ComponentController@showAddComponent',
]);
$router->post('add', 'ComponentController@createComponentAction');
$router->get('groups', [
'as' => 'groups',
'uses' => 'ComponentController@showComponentGroups',
]);
$router->get('groups/add', [
'as' => 'groups.add',
'uses' => 'ComponentController@showAddComponentGroup',
]);
$router->get('groups/edit/{component_group}', [
'as' => 'groups.edit',
'uses' => 'ComponentController@showEditComponentGroup',
]);
$router->post('groups/edit/{component_group}', 'ComponentController@updateComponentGroupAction');
$router->delete('groups/{component_group}/delete', 'ComponentController@deleteComponentGroupAction');
$router->post('groups/add', 'ComponentController@postAddComponentGroup');
$router->get('{component}/edit', [
'as' => 'edit',
'uses' => 'ComponentController@showEditComponent',
]);
$router->delete('{component}/delete', 'ComponentController@deleteComponentAction');
$router->post('{component}/edit', 'ComponentController@updateComponentAction');
});
$router->group(['as' => 'incidents.', 'prefix' => 'incidents'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'IncidentController@showIncidents',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'IncidentController@showAddIncident',
]);
$router->post('add', 'IncidentController@createIncidentAction');
$router->delete('{incident}/delete', [
'as' => 'delete',
'uses' => 'IncidentController@deleteIncidentAction',
]);
$router->get('{incident}/edit', [
'as' => 'edit',
'uses' => 'IncidentController@showEditIncidentAction',
]);
$router->post('{incident}/edit', 'IncidentController@editIncidentAction');
});
$router->group(['as' => 'schedule.', 'prefix' => 'schedule'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'ScheduleController@showIndex',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'ScheduleController@showAddSchedule',
]);
$router->post('add', 'ScheduleController@addScheduleAction');
$router->get('{incident}/edit', [
'as' => 'edit',
'uses' => 'ScheduleController@showEditSchedule',
]);
$router->post('{incident}/edit', 'ScheduleController@editScheduleAction');
$router->delete('{incident}/delete', [
'as' => 'delete',
'uses' => 'ScheduleController@deleteScheduleAction',
]);
});
$router->group(['as' => 'templates.', 'prefix' => 'templates'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'IncidentController@showTemplates',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'IncidentController@showAddIncidentTemplate',
]);
$router->post('add', 'IncidentController@createIncidentTemplateAction');
$router->get('{incident_template}/edit', [
'as' => 'edit',
'uses' => 'IncidentController@showEditTemplateAction',
]);
$router->post('{incident_template}/edit', 'IncidentController@editTemplateAction');
$router->delete('{incident_template}/delete', 'IncidentController@deleteTemplateAction');
});
$router->group(['as' => 'subscribers.', 'prefix' => 'subscribers'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'SubscriberController@showSubscribers',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'SubscriberController@showAddSubscriber',
]);
$router->post('add', 'SubscriberController@createSubscriberAction');
$router->delete('{subscriber}/delete', 'SubscriberController@deleteSubscriberAction');
});
$router->group(['as' => 'metrics.', 'prefix' => 'metrics'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'MetricController@showMetrics',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'MetricController@showAddMetric',
]);
$router->post('add', 'MetricController@createMetricAction');
$router->delete('{metric}/delete', 'MetricController@deleteMetricAction');
$router->get('{metric}/edit', [
'as' => 'edit',
'uses' => 'MetricController@showEditMetricAction',
]);
$router->post('{metric}/edit', 'MetricController@editMetricAction');
});
$router->group(['as' => 'team.', 'prefix' => 'team'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'TeamController@showTeamView',
'uses' => 'DashboardController@showDashboard',
]);
$router->group(['middleware' => 'admin'], function (Registrar $router) {
$router->group(['as' => 'components.', 'prefix' => 'components'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'ComponentController@showComponents',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'TeamController@showAddTeamMemberView',
'uses' => 'ComponentController@showAddComponent',
]);
$router->get('invite', [
'as' => 'invite',
'uses' => 'TeamController@showInviteTeamMemberView',
$router->post('add', 'ComponentController@createComponentAction');
$router->get('groups', [
'as' => 'groups',
'uses' => 'ComponentController@showComponentGroups',
]);
$router->get('{user}', ['as' => 'edit', 'uses' => 'TeamController@showTeamMemberView']);
$router->post('add', 'TeamController@postAddUser');
$router->post('invite', 'TeamController@postInviteUser');
$router->post('{user}', 'TeamController@postUpdateUser');
$router->delete('{user}/delete', 'TeamController@deleteUser');
$router->get('groups/add', [
'as' => 'groups.add',
'uses' => 'ComponentController@showAddComponentGroup',
]);
$router->get('groups/edit/{component_group}', [
'as' => 'groups.edit',
'uses' => 'ComponentController@showEditComponentGroup',
]);
$router->post('groups/edit/{component_group}', 'ComponentController@updateComponentGroupAction');
$router->delete('groups/{component_group}/delete', 'ComponentController@deleteComponentGroupAction');
$router->post('groups/add', 'ComponentController@postAddComponentGroup');
$router->get('{component}/edit', [
'as' => 'edit',
'uses' => 'ComponentController@showEditComponent',
]);
$router->delete('{component}/delete', 'ComponentController@deleteComponentAction');
$router->post('{component}/edit', 'ComponentController@updateComponentAction');
});
});
$router->group(['as' => 'settings.', 'prefix' => 'settings'], function (Registrar $router) {
$router->get('setup', [
'as' => 'setup',
'uses' => 'SettingsController@showSetupView',
]);
$router->get('analytics', [
'as' => 'analytics',
'uses' => 'SettingsController@showAnalyticsView',
]);
$router->get('localization', [
'as' => 'localization',
'uses' => 'SettingsController@showLocalizationView',
]);
$router->get('security', [
'as' => 'security',
'uses' => 'SettingsController@showSecurityView',
]);
$router->get('theme', [
'as' => 'theme',
'uses' => 'SettingsController@showThemeView',
]);
$router->get('stylesheet', [
'as' => 'stylesheet',
'uses' => 'SettingsController@showStylesheetView',
]);
$router->get('customization', [
'as' => 'customization',
'uses' => 'SettingsController@showCustomizationView',
]);
$router->post('/', 'SettingsController@postSettings');
});
$router->group(['as' => 'incidents.', 'prefix' => 'incidents'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'IncidentController@showIncidents',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'IncidentController@showAddIncident',
]);
$router->post('add', 'IncidentController@createIncidentAction');
$router->delete('{incident}/delete', [
'as' => 'delete',
'uses' => 'IncidentController@deleteIncidentAction',
]);
$router->get('{incident}/edit', [
'as' => 'edit',
'uses' => 'IncidentController@showEditIncidentAction',
]);
$router->post('{incident}/edit', 'IncidentController@editIncidentAction');
});
$router->group(['prefix' => 'user'], function (Registrar $router) {
$router->get('/', [
'as' => 'user',
'uses' => 'UserController@showUser',
]);
$router->post('/', 'UserController@postUser');
$router->get('{user}/api/regen', 'UserController@regenerateApiKey');
});
$router->group(['as' => 'schedule.', 'prefix' => 'schedule'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'ScheduleController@showIndex',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'ScheduleController@showAddSchedule',
]);
$router->post('add', 'ScheduleController@addScheduleAction');
$router->get('{incident}/edit', [
'as' => 'edit',
'uses' => 'ScheduleController@showEditSchedule',
]);
$router->post('{incident}/edit', 'ScheduleController@editScheduleAction');
$router->delete('{incident}/delete', [
'as' => 'delete',
'uses' => 'ScheduleController@deleteScheduleAction',
]);
});
$router->group(['prefix' => 'api'], function (Registrar $router) {
$router->get('incidents/templates', 'ApiController@getIncidentTemplate');
$router->post('components/groups/order', 'ApiController@postUpdateComponentGroupOrder');
$router->post('components/order', 'ApiController@postUpdateComponentOrder');
$router->post('components/{component}', 'ApiController@postUpdateComponent');
$router->get('system/version', 'ApiController@checkVersion');
$router->group(['as' => 'templates.', 'prefix' => 'templates'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'IncidentController@showTemplates',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'IncidentController@showAddIncidentTemplate',
]);
$router->post('add', 'IncidentController@createIncidentTemplateAction');
$router->get('{incident_template}/edit', [
'as' => 'edit',
'uses' => 'IncidentController@showEditTemplateAction',
]);
$router->post('{incident_template}/edit', 'IncidentController@editTemplateAction');
$router->delete('{incident_template}/delete', 'IncidentController@deleteTemplateAction');
});
$router->group(['as' => 'subscribers.', 'prefix' => 'subscribers'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'SubscriberController@showSubscribers',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'SubscriberController@showAddSubscriber',
]);
$router->post('add', 'SubscriberController@createSubscriberAction');
$router->delete('{subscriber}/delete', 'SubscriberController@deleteSubscriberAction');
});
$router->group(['as' => 'metrics.', 'prefix' => 'metrics'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'MetricController@showMetrics',
]);
$router->get('add', [
'as' => 'add',
'uses' => 'MetricController@showAddMetric',
]);
$router->post('add', 'MetricController@createMetricAction');
$router->delete('{metric}/delete', 'MetricController@deleteMetricAction');
$router->get('{metric}/edit', [
'as' => 'edit',
'uses' => 'MetricController@showEditMetricAction',
]);
$router->post('{metric}/edit', 'MetricController@editMetricAction');
});
$router->group(['as' => 'team.', 'prefix' => 'team'], function (Registrar $router) {
$router->get('/', [
'as' => 'index',
'uses' => 'TeamController@showTeamView',
]);
$router->group(['middleware' => 'admin'], function (Registrar $router) {
$router->get('add', [
'as' => 'add',
'uses' => 'TeamController@showAddTeamMemberView',
]);
$router->get('invite', [
'as' => 'invite',
'uses' => 'TeamController@showInviteTeamMemberView',
]);
$router->get('{user}', ['as' => 'edit', 'uses' => 'TeamController@showTeamMemberView']);
$router->post('add', 'TeamController@postAddUser');
$router->post('invite', 'TeamController@postInviteUser');
$router->post('{user}', 'TeamController@postUpdateUser');
$router->delete('{user}/delete', 'TeamController@deleteUser');
});
});
$router->group(['as' => 'settings.', 'prefix' => 'settings'], function (Registrar $router) {
$router->get('setup', [
'as' => 'setup',
'uses' => 'SettingsController@showSetupView',
]);
$router->get('analytics', [
'as' => 'analytics',
'uses' => 'SettingsController@showAnalyticsView',
]);
$router->get('localization', [
'as' => 'localization',
'uses' => 'SettingsController@showLocalizationView',
]);
$router->get('security', [
'as' => 'security',
'uses' => 'SettingsController@showSecurityView',
]);
$router->get('theme', [
'as' => 'theme',
'uses' => 'SettingsController@showThemeView',
]);
$router->get('stylesheet', [
'as' => 'stylesheet',
'uses' => 'SettingsController@showStylesheetView',
]);
$router->get('customization', [
'as' => 'customization',
'uses' => 'SettingsController@showCustomizationView',
]);
$router->get('credits', [
'as' => 'credits',
'uses' => 'SettingsController@showCreditsView',
]);
$router->post('/', 'SettingsController@postSettings');
});
$router->group(['prefix' => 'user'], function (Registrar $router) {
$router->get('/', [
'as' => 'user',
'uses' => 'UserController@showUser',
]);
$router->post('/', 'UserController@postUser');
$router->get('{user}/api/regen', 'UserController@regenerateApiKey');
});
$router->group(['prefix' => 'api'], function (Registrar $router) {
$router->get('incidents/templates', 'ApiController@getIncidentTemplate');
$router->post('components/groups/order', 'ApiController@postUpdateComponentGroupOrder');
$router->post('components/order', 'ApiController@postUpdateComponentOrder');
$router->post('components/{component}', 'ApiController@postUpdateComponent');
});
});
});
}

View File

@@ -17,6 +17,7 @@ use Illuminate\Contracts\Routing\Registrar;
* This is the setup routes class.
*
* @author James Brooks <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class SetupRoutes
{
@@ -30,7 +31,10 @@ class SetupRoutes
public function map(Registrar $router)
{
$router->group(['middleware' => ['web', 'setup']], function (Registrar $router) {
$router->controller('setup', 'SetupController');
$router->get('setup', 'SetupController@getIndex');
$router->post('setup/step1', 'SetupController@postStep1');
$router->post('setup/step2', 'SetupController@postStep2');
$router->post('setup/step3', 'SetupController@postStep3');
});
}
}

View File

@@ -44,6 +44,11 @@ class SubscribeRoutes
'uses' => 'SubscribeController@showManage',
]);
$router->post('subscribe/manage/{code}', [
'as' => 'manage',
'uses' => 'SubscribeController@postManage',
]);
$router->get('subscribe/verify/{code}', [
'as' => 'verify',
'uses' => 'SubscribeController@getVerify',

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Integrations\Contracts;
/**
* This is the system interface.
*
* @author James Brooks <james@alt-three.com>
*/
interface System
{
/**
* Get the entire system status.
*
* @return array
*/
public function getStatus();
}

View File

@@ -0,0 +1,70 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Integrations\Core;
use CachetHQ\Cachet\Integrations\Contracts\System as SystemContract;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Incident;
/**
* This is the core system class.
*
* @author James Brooks <james@alt-three.com>
*/
class System implements SystemContract
{
/**
* Get the entire system status.
*
* @return array
*/
public function getStatus()
{
$enabledScope = Component::enabled();
$totalComponents = Component::enabled()->count();
$majorOutages = Component::enabled()->status(4)->count();
$isMajorOutage = $totalComponents ? ($majorOutages / $totalComponents) >= 0.5 : false;
// Default data
$status = [
'system_status' => 'info',
'system_message' => trans_choice('cachet.service.bad', $totalComponents),
'favicon' => 'favicon-high-alert',
];
if ($isMajorOutage) {
$status = [
'system_status' => 'danger',
'system_message' => trans_choice('cachet.service.major', $totalComponents),
'favicon' => 'favicon-high-alert',
];
} elseif (Component::enabled()->notStatus(1)->count() === 0) {
// If all our components are ok, do we have any non-fixed incidents?
$incidents = Incident::notScheduled()->orderBy('created_at', 'desc')->get()->filter(function ($incident) {
return $incident->status > 0;
});
$incidentCount = $incidents->count();
if ($incidentCount === 0 || ($incidentCount >= 1 && (int) $incidents->first()->status === 4)) {
$status = [
'system_status' => 'success',
'system_message' => trans_choice('cachet.service.good', $totalComponents),
'favicon' => 'favicon',
];
}
} elseif (Component::enabled()->whereIn('status', [2, 3])->count() > 0) {
$status['favicon'] = 'favicon-medium-alert';
}
return $status;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Integrations;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Cache\Repository;
class Credits
{
/**
* The default url.
*
* @var string
*/
const URL = 'https://cachethq.io/credits';
/**
* The failed status indicator.
*
* @var int
*/
const FAILED = 1;
/**
* The cache repository instance.
*
* @var \Illuminate\Contracts\Cache\Repository
*/
protected $cache;
/**
* The url to use.
*
* @var string|null
*/
protected $url;
/**
* Creates a new credits instance.
*
* @param \Illuminate\Contracts\Cache\Repository $cache
* @param string|null $url
*
* @return void
*/
public function __construct(Repository $cache, $url = null)
{
$this->cache = $cache;
$this->url = $url ?: static::URL;
}
/**
* Returns the latest credits.
*
* @return array|null
*/
public function latest()
{
$result = $this->cache->remember('credits', 2880, function () {
try {
return json_decode((new Client())->get($this->url, [
'headers' => ['Accept' => 'application/json', 'User-Agent' => defined('CACHET_VERSION') ? 'cachet/'.constant('CACHET_VERSION') : 'cachet'],
])->getBody(), true);
} catch (Exception $e) {
return self::FAILED;
}
});
return $result === self::FAILED ? null : $result;
}
}

88
app/Integrations/Feed.php Normal file
View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Integrations;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Cache\Repository;
/**
* This is the feed class.
*
* @author James Brooks <james@alt-three.com>
*/
class Feed
{
/**
* The default url.
*
* @var string
*/
const URL = 'https://blog.alt-three.com/tag/cachet/rss';
/**
* The failed status indicator.
*
* @var int
*/
const FAILED = 1;
/**
* The cache repository instance.
*
* @var \Illuminate\Contracts\Cache\Repository
*/
protected $cache;
/**
* The url to use.
*
* @var string|null
*/
protected $url;
/**
* Creates a new feed instance.
*
* @param \Illuminate\Contracts\Cache\Repository $cache
* @param string|null $url
*
* @return void
*/
public function __construct(Repository $cache, $url = null)
{
$this->cache = $cache;
$this->url = $url ?: static::URL;
}
/**
* Returns the latest entries.
*
* @return array|null
*/
public function latest()
{
$result = $this->cache->remember('feeds', 720, function () {
try {
$xml = simplexml_load_string((new Client())->get($this->url, [
'headers' => ['Accept' => 'application/rss+xml', 'User-Agent' => defined('CACHET_VERSION') ? 'cachet/'.constant('CACHET_VERSION') : 'cachet'],
])->getBody()->getContents(), null, LIBXML_NOCDATA);
return json_decode(json_encode($xml));
} catch (Exception $e) {
return self::FAILED;
}
});
return $result === self::FAILED ? null : $result;
}
}

View File

@@ -0,0 +1,95 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Integrations;
use GuzzleHttp\Client;
use Illuminate\Contracts\Cache\Repository;
class Releases
{
/**
* The default url.
*
* @var string
*/
const URL = 'https://api.github.com/repos/cachethq/cachet/releases/latest';
/**
* The failed status indicator.
*
* @var int
*/
const FAILED = 1;
/**
* The cache repository instance.
*
* @var \Illuminate\Contracts\Cache\Repository
*/
protected $cache;
/**
* The github authentication token.
*
* @var string|null
*/
protected $token;
/**
* The url to use.
*
* @var string|null
*/
protected $url;
/**
* Creates a new releases instance.
*
* @param \Illuminate\Contracts\Cache\Repository $cache
* @param string|null $token
* @param string|null $url
*
* @return void
*/
public function __construct(Repository $cache, $token = null, $url = null)
{
$this->cache = $cache;
$this->token = $token;
$this->url = $url ?: static::URL;
}
/**
* Returns the latest release.
*
* @return string
*/
public function latest()
{
$release = $this->cache->remember('release.latest', 720, function () {
$headers = ['Accept' => 'application/vnd.github.v3+json', 'User-Agent' => defined('CACHET_VERSION') ? 'cachet/'.constant('CACHET_VERSION') : 'cachet'];
if ($this->token) {
$headers['OAUTH-TOKEN'] = $this->token;
}
return json_decode((new Client())->get($this->url, [
'headers' => $headers,
])->getBody(), true);
});
return [
'tag_name' => $release['tag_name'],
'prelease' => $release['prerelease'],
'draft' => $release['draft'],
];
}
}

View File

@@ -99,7 +99,7 @@ class ComponentGroup extends Model implements HasPresenter
*/
public function components()
{
return $this->hasMany(Component::class, 'group_id', 'id');
return $this->hasMany(Component::class, 'group_id', 'id')->orderBy('order');
}
/**

View File

@@ -16,6 +16,7 @@ use CachetHQ\Cachet\Models\Traits\SearchableTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use CachetHQ\Cachet\Presenters\IncidentPresenter;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use McCool\LaravelAutoPresenter\HasPresenter;
@@ -71,6 +72,7 @@ class Incident extends Model implements HasPresenter
*/
protected $searchable = [
'id',
'component_id',
'name',
'status',
'visible',
@@ -96,7 +98,7 @@ class Incident extends Model implements HasPresenter
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeVisible($query)
public function scopeVisible(Builder $query)
{
return $query->where('visible', 1);
}
@@ -108,9 +110,9 @@ class Incident extends Model implements HasPresenter
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeScheduled($query)
public function scopeScheduled(Builder $query)
{
return $query->where('status', 0)->where('scheduled_at', '>=', Carbon::now());
return $query->where('status', 0)->where('scheduled_at', '>=', Carbon::now()->toDateTimeString());
}
/**
@@ -120,10 +122,12 @@ class Incident extends Model implements HasPresenter
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeNotScheduled($query)
public function scopeNotScheduled(Builder $query)
{
return $query->where(function ($query) {
return $query->whereNull('scheduled_at')->orWhere('scheduled_at', '<=', Carbon::now());
return $query->where('status', '>', 0)->orWhere(function ($query) {
$query->where('status', 0)->where(function ($query) {
$query->whereNull('scheduled_at')->orWhere('scheduled_at', '<=', Carbon::now()->toDateTimeString());
});
});
}

View File

@@ -14,6 +14,7 @@ namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use CachetHQ\Cachet\Presenters\MetricPresenter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use McCool\LaravelAutoPresenter\HasPresenter;
@@ -47,6 +48,8 @@ class Metric extends Model implements HasPresenter
'calc_type' => 0,
'places' => 2,
'default_view' => 1,
'threshold' => 5,
'order' => 0,
];
/**
@@ -61,6 +64,8 @@ class Metric extends Model implements HasPresenter
'calc_type' => 'int',
'places' => 'int',
'default_view' => 'int',
'threshold' => 'int',
'order' => 'int',
];
/**
@@ -77,6 +82,8 @@ class Metric extends Model implements HasPresenter
'calc_type',
'places',
'default_view',
'threshold',
'order',
];
/**
@@ -91,6 +98,8 @@ class Metric extends Model implements HasPresenter
'default_value' => 'numeric',
'places' => 'numeric|between:0,4',
'default_view' => 'numeric|between:0,3',
'threshold' => 'numeric|between:0,10',
'threshold' => 'int',
];
/**
@@ -104,6 +113,7 @@ class Metric extends Model implements HasPresenter
'display_chart',
'default_value',
'calc_type',
'order',
];
/**
@@ -116,6 +126,18 @@ class Metric extends Model implements HasPresenter
return $this->hasMany(MetricPoint::class, 'metric_id', 'id');
}
/**
* Scope metrics to those of which are displayable.
*
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeDisplayable(Builder $query)
{
return $query->where('display_chart', 1);
}
/**
* Determines whether a chart should be shown.
*

View File

@@ -20,6 +20,16 @@ class MetricPoint extends Model implements HasPresenter
{
use ValidatingTrait;
/**
* The model's attributes.
*
* @var string[]
*/
protected $attributes = [
'value' => 0,
'counter' => 1,
];
/**
* The attributes that should be casted to native types.
*
@@ -27,7 +37,8 @@ class MetricPoint extends Model implements HasPresenter
*/
protected $casts = [
'metric_id' => 'int',
'value' => 'int',
'value' => 'float',
'counter' => 'int',
];
/**
@@ -35,7 +46,12 @@ class MetricPoint extends Model implements HasPresenter
*
* @var string[]
*/
protected $fillable = ['metric_id', 'value', 'created_at'];
protected $fillable = [
'metric_id',
'value',
'counter',
'created_at',
];
/**
* The validation rules.
@@ -53,7 +69,25 @@ class MetricPoint extends Model implements HasPresenter
*/
public function metric()
{
return $this->belongsTo(Metric::class, 'id', 'metric_id');
return $this->belongsTo(Metric::class);
}
/**
* Override the value attribute.
*
* @param mixed $value
*
* @return float
*/
public function getActiveValueAttribute($value)
{
if ($this->metric->calc_type === Metric::CALC_SUM) {
return round((float) $value * $this->counter, $this->metric->places);
} elseif ($this->metric->calc_type === Metric::CALC_AVG) {
return round((float) $value * $this->counter, $this->metric->places);
}
return round((float) $value, $this->metric->places);
}
/**

View File

@@ -30,6 +30,7 @@ class Subscriber extends Model implements HasPresenter
'email' => 'string',
'verify_code' => 'string',
'verified_at' => 'date',
'global' => 'bool',
];
/**
@@ -91,6 +92,33 @@ class Subscriber extends Model implements HasPresenter
return $query->whereNotNull('verified_at');
}
/**
* Scope global subscribers.
*
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeIsGlobal(Builder $query)
{
return $query->where('global', true);
}
/**
* Finds all verified subscriptions for a component.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param int $component_id
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeForComponent(Builder $query, $component_id)
{
return $query->select('subscribers.*')
->join('subscriptions', 'subscribers.id', '=', 'subscriptions.subscriber_id')
->where('subscriptions.component_id', $component_id);
}
/**
* Determines if the subscriber is verified.
*

View File

@@ -107,7 +107,10 @@ class Subscription extends Model
{
return $query->select('subscriptions.*')
->join('subscribers', 'subscriptions.subscriber_id', '=', 'subscribers.id')
->where('component_id', $component_id)
->where(function ($query) {
$query->where('subscriptions.component_id', $component_id)
->orWhere('subscribers.global');
})
->whereNotNull('subscribers.verified_at');
}
}

View File

@@ -53,7 +53,7 @@ class ComponentPresenter extends BasePresenter implements Arrayable
*/
public function tags()
{
return $this->wrappedObject->tags->lists('name', 'slug');
return $this->wrappedObject->tags->pluck('name', 'slug');
}
/**

View File

@@ -19,6 +19,16 @@ class MetricPointPresenter extends BasePresenter implements Arrayable
{
use TimestampsTrait;
/**
* Show the actual calculated value; as per (value * counter).
*
* @return int
*/
public function calculated_value()
{
return $this->wrappedObject->value * $this->wrappedObject->counter;
}
/**
* Convert the presenter instance to an array.
*
@@ -27,8 +37,9 @@ class MetricPointPresenter extends BasePresenter implements Arrayable
public function toArray()
{
return array_merge($this->wrappedObject->toArray(), [
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),
'calculated_value' => $this->calculated_value(),
]);
}
}

View File

@@ -28,17 +28,7 @@ class SubscriberPresenter extends BasePresenter implements Arrayable
*/
public function verified_at()
{
return app(DateFactory::class)->make($this->wrappedObject->verified_at)->toDateTimeString();
}
/**
* Present formatted subscribed date.
*
* @return string
*/
public function subscribed_at()
{
return ucfirst(app(DateFactory::class)->make($this->wrappedObject->subscribed_at)->format(Config::get('setting.incident_date_format', 'l jS F Y H:i:s')));
return ucfirst(app(DateFactory::class)->make($this->wrappedObject->verified_at)->format(Config::get('setting.incident_date_format', 'l jS F Y H:i:s')));
}
/**

View File

@@ -0,0 +1,63 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Repositories\Metric;
use CachetHQ\Cachet\Models\Metric;
use Illuminate\Contracts\Config\Repository;
/**
* This is the abstract metric repository class.
*
* @author Jams Brooks <james@alt-three.com>
*/
abstract class AbstractMetricRepository
{
/**
* The illuminate config repository.
*
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* The name of the metrics table.
*
* @var string
*/
protected $tableName;
/**
* Create a new abstract metric repository instance.
*
* @param \Illuminate\Contracts\Config\Repository $config
*
* @return void
*/
public function __construct(Repository $config)
{
$this->config = $config;
}
/**
* Get the metrics table name.
*
* @return string
*/
protected function getTableName()
{
$driver = $this->config->get('database.default');
$connection = $this->config->get('database.connections.'.$driver);
$prefix = $connection['prefix'];
return $prefix.'metrics';
}
}

View File

@@ -105,7 +105,7 @@ class MetricRepository
$points = [];
$pointKey = $dateTime->format('jS M');
$pointKey = $dateTime->format('D jS M');
for ($i = 0; $i <= 7; $i++) {
$points[$pointKey] = $this->repository->getPointsForDayInWeek($metric, $i);

View File

@@ -16,7 +16,12 @@ use DateInterval;
use Illuminate\Support\Facades\DB;
use Jenssegers\Date\Date;
class MySqlRepository implements MetricInterface
/**
* This is the mysql repository class.
*
* @author James Brooks <james@alt-three.com>
*/
class MySqlRepository extends AbstractMetricRepository implements MetricInterface
{
/**
* Returns metrics for the last hour.
@@ -32,14 +37,21 @@ class MySqlRepository implements MetricInterface
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'))->sub(new DateInterval('PT'.$minute.'M'));
$timeInterval = $dateTime->format('YmdHi');
$points = $metric->points()
->whereRaw('DATE_FORMAT(created_at, "%Y%m%d%H%i") = '.$timeInterval)
->groupBy(DB::raw('HOUR(created_at), MINUTE(created_at)'));
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'SUM(mp.`value` * mp.`counter`) AS `value`';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'AVG(mp.`value` * mp.`counter`) AS `value`';
}
$value = 0;
$points = DB::select("SELECT {$queryType} FROM {$this->getTableName()} m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND DATE_FORMAT(mp.`created_at`, '%Y%m%d%H%i') = :timeInterval GROUP BY HOUR(mp.`created_at`), MINUTE(mp.`created_at`)", [
'metricId' => $metric->id,
'timeInterval' => $timeInterval,
]);
if (isset($points[0]) && !($value = $points[0]->value)) {
$value = 0;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -62,14 +74,21 @@ class MySqlRepository implements MetricInterface
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
$hourInterval = $dateTime->format('YmdH');
$points = $metric->points()
->whereRaw('DATE_FORMAT(created_at, "%Y%m%d%H") = '.$hourInterval)
->groupBy(DB::raw('HOUR(created_at)'));
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'SUM(mp.`value` * mp.`counter`) AS `value`';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'AVG(mp.`value` * mp.`counter`) AS `value`';
}
$value = 0;
$points = DB::select("SELECT {$queryType} FROM {$this->getTableName()} m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND DATE_FORMAT(mp.`created_at`, '%Y%m%d%H') = :hourInterval GROUP BY HOUR(mp.`created_at`)", [
'metricId' => $metric->id,
'hourInterval' => $hourInterval,
]);
if (isset($points[0]) && !($value = $points[0]->value)) {
$value = 0;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -90,15 +109,21 @@ class MySqlRepository implements MetricInterface
{
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
$points = $metric->points()
->whereRaw('created_at BETWEEN DATE_SUB(created_at, INTERVAL 1 WEEK) AND NOW()')
->whereRaw('DATE_FORMAT(created_at, "%Y%m%d") = '.$dateTime->format('Ymd'))
->groupBy(DB::raw('DATE_FORMAT(created_at, "%Y%m%d")'));
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'SUM(mp.`value` * mp.`counter`) AS `value`';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'AVG(mp.`value` * mp.`counter`) AS `value`';
}
$value = 0;
$points = DB::select("SELECT {$queryType} FROM {$this->getTableName()} m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND mp.`created_at` BETWEEN DATE_SUB(mp.`created_at`, INTERVAL 1 WEEK) AND DATE_ADD(NOW(), INTERVAL 1 DAY) AND DATE_FORMAT(mp.`created_at`, '%Y%m%d') = :timeInterval GROUP BY DATE_FORMAT(mp.`created_at`, '%Y%m%d')", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('Ymd'),
]);
if (isset($points[0]) && !($value = $points[0]->value)) {
$value = 0;
}
if ($value === 0 && $metric->default_value != $value) {

View File

@@ -16,7 +16,12 @@ use DateInterval;
use Illuminate\Support\Facades\DB;
use Jenssegers\Date\Date;
class PgSqlRepository implements MetricInterface
/**
* This is the pgsql repository class.
*
* @author James Brooks <james@alt-three.com>
*/
class PgSqlRepository extends AbstractMetricRepository implements MetricInterface
{
/**
* Returns metrics for the last hour.
@@ -30,26 +35,24 @@ class PgSqlRepository implements MetricInterface
public function getPointsLastHour(Metric $metric, $hour, $minute)
{
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'))->sub(new DateInterval('PT'.$minute.'M'));
$hourInterval = $dateTime->format('YmdHi');
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$queryType = 'sum(metric_points.value)';
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$queryType = 'avg(metric_points.value)';
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value)';
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$query = DB::select("select {$queryType} as aggregate FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metric_id AND to_char(metric_points.created_at, 'YYYYMMDDHH24MI') = :timestamp GROUP BY to_char(metric_points.created_at, 'HHMI')", [
'metric_id' => $metric->id,
'timestamp' => $hourInterval,
$value = 0;
$query = DB::select("select {$queryType} as value FROM {$this->getTableName()} m JOIN metric_points ON metric_points.metric_id = m.id WHERE m.id = :metricId AND to_char(metric_points.created_at, 'YYYYMMDDHH24MI') = :timeInterval GROUP BY to_char(metric_points.created_at, 'HHMI')", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('YmdHi'),
]);
if (isset($query[0])) {
$value = $query[0]->aggregate;
} else {
$value = 0;
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -70,26 +73,24 @@ class PgSqlRepository implements MetricInterface
public function getPointsByHour(Metric $metric, $hour)
{
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
$hourInterval = $dateTime->format('YmdH');
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$queryType = 'sum(metric_points.value)';
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$queryType = 'avg(metric_points.value)';
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value)';
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$query = DB::select("select {$queryType} as aggregate FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metric_points.metric_id = :metric_id AND to_char(metric_points.created_at, 'YYYYMMDDHH24') = :timestamp GROUP BY to_char(metric_points.created_at, 'H')", [
'metric_id' => $metric->id,
'timestamp' => $hourInterval,
$value = 0;
$query = DB::select("select {$queryType} as value FROM {$this->getTableName()} m JOIN metric_points ON metric_points.metric_id = m.id WHERE metric_points.metric_id = :metricId AND to_char(metric_points.created_at, 'YYYYMMDDHH24') = :timeInterval GROUP BY to_char(metric_points.created_at, 'H')", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('YmdH'),
]);
if (isset($query[0])) {
$value = $query[0]->aggregate;
} else {
$value = 0;
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -110,15 +111,20 @@ class PgSqlRepository implements MetricInterface
{
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
$points = $metric->points()
->whereRaw('created_at BETWEEN (created_at - interval \'1 week\') AND now()')
->whereRaw('to_char(created_at, \'YYYYMMDD\') = \''.$dateTime->format('Ymd').'\'')
->groupBy(DB::raw('to_char(created_at, \'YYYYMMDD\')'));
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'sum(mp.value * mp.counter) AS value';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'avg(mp.value * mp.counter) AS value';
}
$value = 0;
$points = DB::select("SELECT {$queryType} FROM {$this->getTableName()} m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND mp.created_at BETWEEN (mp.created_at - interval '1 week') AND (now() + interval '1 day') AND to_char(mp.created_at, 'YYYYMMDD') = :timeInterval GROUP BY to_char(mp.created_at, 'YYYYMMDD')", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('Ymd'),
]);
if (isset($points[0]) && !($value = $points[0]->value)) {
$value = 0;
}
if ($value === 0 && $metric->default_value != $value) {

View File

@@ -16,7 +16,7 @@ use DateInterval;
use Illuminate\Support\Facades\DB;
use Jenssegers\Date\Date;
class SqliteRepository implements MetricInterface
class SqliteRepository extends AbstractMetricRepository implements MetricInterface
{
/**
* Returns metrics for the last hour.
@@ -30,16 +30,24 @@ class SqliteRepository implements MetricInterface
public function getPointsLastHour(Metric $metric, $hour, $minute)
{
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'))->sub(new DateInterval('PT'.$minute.'M'));
$hourInterval = $dateTime->format('YmdHi');
$points = $metric->points()
->whereRaw('strftime("%Y%m%d%H%M", created_at) = "'.$hourInterval.'"')
->groupBy(DB::raw('strftime("%H%M", created_at)'));
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$value = 0;
$query = DB::select("select {$queryType} as value FROM {$this->getTableName()} m JOIN metric_points ON metric_points.metric_id = m.id WHERE m.id = :metricId AND strftime('%Y%m%d%H%M', metric_points.created_at) = :timeInterval GROUP BY strftime('%H%M', metric_points.created_at)", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('YmdHi'),
]);
if (isset($query[0])) {
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -60,16 +68,24 @@ class SqliteRepository implements MetricInterface
public function getPointsByHour(Metric $metric, $hour)
{
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
$hourInterval = $dateTime->format('YmdH');
$points = $metric->points()
->whereRaw('strftime("%Y%m%d%H", created_at) = "'.$hourInterval.'"')
->groupBy(DB::raw('strftime("%H", created_at)'));
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$value = 0;
$query = DB::select("select {$queryType} as value FROM {$this->getTableName()} m JOIN metric_points ON metric_points.metric_id = m.id WHERE m.id = :metricId AND strftime('%Y%m%d%H', metric_points.created_at) = :timeInterval GROUP BY strftime('%H', metric_points.created_at)", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('YmdH'),
]);
if (isset($query[0])) {
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {
@@ -90,15 +106,23 @@ class SqliteRepository implements MetricInterface
{
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
$points = $metric->points()
->whereRaw('created_at > date("now", "-7 day")')
->whereRaw('strftime("%Y%m%d", created_at) = "'.$dateTime->format('Ymd').'"')
->groupBy(DB::raw('strftime("%Y%m%d", created_at)'));
// Default metrics calculations.
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
$value = $points->sum('value');
$queryType = 'sum(metric_points.value * metric_points.counter)';
} elseif ($metric->calc_type == Metric::CALC_AVG) {
$value = $points->avg('value');
$queryType = 'avg(metric_points.value * metric_points.counter)';
} else {
$queryType = 'sum(metric_points.value * metric_points.counter)';
}
$value = 0;
$query = DB::select("select {$queryType} as value FROM {$this->getTableName()} m JOIN metric_points ON metric_points.metric_id = m.id WHERE m.id = :metricId AND metric_points.created_at > date('now', '-7 day') AND strftime('%Y%m%d', metric_points.created_at) = :timeInterval GROUP BY strftime('%Y%m%d', metric_points.created_at)", [
'metricId' => $metric->id,
'timeInterval' => $dateTime->format('Ymd'),
]);
if (isset($query[0])) {
$value = $query[0]->value;
}
if ($value === 0 && $metric->default_value != $value) {

103
app/Settings/Cache.php Normal file
View File

@@ -0,0 +1,103 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Settings;
use Exception;
use Illuminate\Filesystem\Filesystem;
/**
* This is the settings cache class.
*
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
*/
class Cache
{
/**
* The filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $files;
/**
* Is path to the setting cache.
*
* @var string
*/
protected $path;
/**
* Create a new settings cache instance.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @param string $path
*
* @return void
*/
public function __construct(Filesystem $files, $path)
{
$this->files = $files;
$this->path = $path;
}
/**
* Store the settings in the cache.
*
* @param string $env
* @param array $data
*
* @return void
*/
public function store($env, array $data)
{
$this->files->put($this->path($env), '<?php return '.var_export($data, true).';'.PHP_EOL);
}
/**
* Load the settings from the cache.
*
* @param string $env
*
* @return array|false
*/
public function load($env)
{
try {
return $this->files->getRequire($this->path($env));
} catch (Exception $e) {
return false;
}
}
/**
* Clear the settings cache.
*
* Note that we're careful not to remove the .gitignore file.
*
* @return void
*/
public function clear()
{
$this->files->delete($this->files->allFiles($this->path));
}
/**
* Returns the settings cache path.
*
* @return string
*/
protected function path($env)
{
return "{$this->path}/{$env}.php";
}
}

View File

@@ -9,10 +9,16 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Config;
namespace CachetHQ\Cachet\Settings;
use CachetHQ\Cachet\Models\Setting;
/**
* This is the settings repository class.
*
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
*/
class Repository
{
/**
@@ -30,7 +36,7 @@ class Repository
protected $stale = false;
/**
* Create a new settings service instance.
* Create a new settings repository instance.
*
* @param \CachetHQ\Cachet\Models\Setting $model
*
@@ -84,6 +90,18 @@ class Repository
$this->model->where('name', $name)->delete();
}
/**
* Clear all settings.
*
* @return void
*/
public function clear()
{
$this->stale = true;
$this->model->query()->delete();
}
/**
* Is the config state stale?
*

View File

@@ -0,0 +1,110 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Subscribers;
use CachetHQ\Cachet\Settings\Cache;
use Carbon\Carbon;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Events\Dispatcher;
/**
* This is the command subscriber class.
*
* @author James Brooks <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class CommandSubscriber
{
/**
* The settings cache instance.
*
* @var \CachetHQ\Cachet\Settings\Cache
*/
protected $cache;
/**
* The config repository instance.
*
* @var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* Create a new command subscriber instance.
*
* @param \CachetHQ\Cachet\Settings\Cache $cache
* @param \Illuminate\Contracts\Config\Repository $config
*
* @return void
*/
public function __construct(Cache $cache, Repository $config)
{
$this->cache = $cache;
$this->config = $config;
}
/**
* Register the listeners for the subscriber.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
*
* @return void
*/
public function subscribe(Dispatcher $events)
{
$events->listen('command.installing', __CLASS__.'@fire', 5);
$events->listen('command.updating', __CLASS__.'@fire', 5);
$events->listen('command.resetting', __CLASS__.'@fire', 5);
}
/**
* Clear the settings cache, and backup the databases.
*
* @param \Illuminate\Console\Command $command
*
* @return void
*/
public function fire(Command $command)
{
$command->line('Clearing settings cache...');
$this->cache->clear();
$command->line('Settings cache cleared!');
// SQLite does not backup.
if ($this->config->get('database.default') === 'sqlite') {
$command->line('Backup skipped: SQLite is not supported.');
return;
}
$command->line('Backing up database...');
try {
$command->call('db:backup', [
'--compression' => 'gzip',
'--database' => $this->config->get('database.default'),
'--destination' => 'local',
'--destinationPath' => Carbon::now()->format('Y-m-d H.i.s'),
'--no-interaction' => true,
]);
} catch (Exception $e) {
$command->error($e->getMessage());
$command->line('Backup skipped!');
}
$command->line('Backup completed!');
}
}

View File

@@ -14,7 +14,6 @@ use Illuminate\Support\Facades\Request;
use Jenssegers\Date\Date;
if (!function_exists('set_active')) {
/**
* Set active class if request is in path.
*

0
bootstrap/cache/.gitignore vendored Executable file → Normal file
View File

2
bootstrap/cachet/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

View File

@@ -1,23 +0,0 @@
{
"name": "Cachet",
"dependencies": {
"animate-sass": "~0.6",
"autosize": "~3.0.15",
"bootstrap-sass": "~3.3",
"chartjs": "~2.0.2",
"eonasdan-bootstrap-datetimepicker": "~3.1",
"humane-js": "~3.2",
"ionicons": "~2.0",
"jquery": "~2.2",
"jquery-minicolors": "~2.2",
"jquery-serialize-object": "~2.5",
"jquery-sparkline": "~2.1",
"livestampjs": "~1.1",
"lodash": "~4.5.1",
"messenger": "~1.4",
"moment": "~2.11.2",
"Sortable": "~1.4",
"sweetalert": "~1.1",
"github-markdown-css": "^2.2.1"
}
}

View File

@@ -20,31 +20,36 @@
],
"require": {
"php": ">=5.5.9",
"laravel/framework": "~5.2.31",
"alt-three/badger": "^3.0",
"alt-three/bus": "^1.0",
"alt-three/emoji": "^3.0",
"alt-three/validator": "^1.4",
"ext-xml": "*",
"laravel/framework": "5.2.39",
"alt-three/badger": "^3.1",
"alt-three/bus": "^1.1",
"alt-three/emoji": "^3.1",
"alt-three/throttle": "^1.0",
"alt-three/validator": "^1.5",
"aws/aws-sdk-php": "^3.7",
"backup-manager/laravel": "dev-master#df53f9c9d8c6be5d7a2638f45d54b8fb7bc51e2b",
"barryvdh/laravel-cors": "^0.8",
"doctrine/dbal": "^2.5",
"fedeisas/laravel-mail-css-inliner": "^1.5",
"fideloper/proxy": "^3.1",
"graham-campbell/binput": "^3.3",
"graham-campbell/core": "^4.2",
"graham-campbell/markdown": "^6.0",
"graham-campbell/exceptions": "^8.3",
"guzzlehttp/guzzle": "^6.2",
"jenssegers/date": "^3.1",
"mccool/laravel-auto-presenter": "^4.2",
"graham-campbell/binput": "^3.4",
"graham-campbell/core": "^5.1",
"graham-campbell/exceptions": "^8.6",
"graham-campbell/markdown": "^6.1",
"guzzlehttp/guzzle": "^6.2.1",
"jenssegers/date": "^3.2",
"mccool/laravel-auto-presenter": "^4.3",
"pragmarx/google2fa": "^0.7.1",
"rcrowe/twigbridge": "^0.9.2",
"roumen/feed": "^2.10"
"roumen/feed": "^2.10.4"
},
"require-dev": {
"alt-three/testbench": "^1.1",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.5",
"alt-three/testbench": "^1.7",
"filp/whoops": "^2.1",
"fzaninotto/faker": "^1.6",
"graham-campbell/testbench-core": "^1.1",
"mockery/mockery": "0.9.4",
"mockery/mockery": "0.9.9",
"phpunit/phpunit": "4.8.21",
"symfony/css-selector": "^3.0",
"symfony/dom-crawler": "^3.0"
@@ -54,7 +59,7 @@
"database"
],
"files": [
"app/Http/helpers.php"
"app/helpers.php"
],
"psr-4": {
"CachetHQ\\Cachet\\": "app/"
@@ -94,7 +99,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
"dev-master": "2.3-dev"
}
},
"minimum-stability": "dev",

1656
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -162,7 +162,9 @@ return [
*/
'AltThree\Badger\BadgerServiceProvider',
'AltThree\Emoji\EmojiServiceProvider',
'BackupManager\Laravel\Laravel5ServiceProvider',
'Barryvdh\Cors\ServiceProvider',
'Fedeisas\LaravelMailCssInliner\LaravelMailCssInlinerServiceProvider',
'Fideloper\Proxy\TrustedProxyServiceProvider',
'GrahamCampbell\Binput\BinputServiceProvider',
'GrahamCampbell\Exceptions\ExceptionsServiceProvider',
@@ -180,9 +182,10 @@ return [
*/
'CachetHQ\Cachet\Foundation\Providers\AppServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\ComposerServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\ConsoleServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\ConfigServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\IntegrationServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\EventServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\GitHubServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\RepositoryServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\RouteServiceProvider',
@@ -231,8 +234,8 @@ return [
'Validator' => 'Illuminate\Support\Facades\Validator',
'View' => 'Illuminate\Support\Facades\View',
'Binput' => 'GrahamCampbell\Binput\Facades\Binput',
'Str' => 'Illuminate\Support\Str',
'Binput' => 'GrahamCampbell\Binput\Facades\Binput',
'Str' => 'Illuminate\Support\Str',
],

70
config/backup-manager.php Normal file
View File

@@ -0,0 +1,70 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
'local' => [
'type' => 'Local',
'root' => database_path('backups'),
],
's3' => [
'type' => 'AwsS3',
'key' => '',
'secret' => '',
'region' => 'us-east-1',
'bucket' => '',
'root' => '',
],
'gcs' => [
'type' => 'Gcs',
'key' => '',
'secret' => '',
'bucket' => '',
'root' => '',
],
'rackspace' => [
'type' => 'Rackspace',
'username' => '',
'key' => '',
'container' => '',
'zone' => '',
'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/',
'root' => '',
],
'dropbox' => [
'type' => 'Dropbox',
'token' => '',
'key' => '',
'secret' => '',
'app' => '',
'root' => '',
],
'ftp' => [
'type' => 'Ftp',
'host' => '',
'username' => '',
'password' => '',
'port' => 21,
'passive' => true,
'ssl' => true,
'timeout' => 30,
'root' => '',
],
'sftp' => [
'type' => 'Sftp',
'host' => '',
'username' => '',
'password' => '',
'port' => 21,
'timeout' => 10,
'privateKey' => '',
'root' => '',
],
];

41
config/css-inliner.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
/*
|--------------------------------------------------------------------------
| Strip styles
|--------------------------------------------------------------------------
|
| Settings this to false prevents the inliner from removing the style
| definitions that have been inlined.
|
| Notice that media query styles are not inlined, and hence never
| stripped.
|
*/
'strip-styles' => true,
/*
|--------------------------------------------------------------------------
| Remove classes
|--------------------------------------------------------------------------
|
| Settings this to false disables the removal of class attributes from
| your html elements (do not enable this if you use media queries)
|
*/
'strip-classes' => true,
];

View File

@@ -76,25 +76,25 @@ return [
],
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', null),
'database' => env('DB_DATABASE', null),
'username' => env('DB_USERNAME', null),
'password' => env('DB_PASSWORD', null),
'port' => env('DB_PORT', '5432'),
'charset' => 'utf8',
'prefix' => env('DB_PREFIX', null),
'schema' => env('DB_SCHEMA', 'public'),
'driver' => 'pgsql',
'host' => env('DB_HOST', null),
'database' => env('DB_DATABASE', null),
'username' => env('DB_USERNAME', null),
'password' => env('DB_PASSWORD', null),
'port' => env('DB_PORT', '5432'),
'charset' => 'utf8',
'prefix' => env('DB_PREFIX', null),
'schema' => env('DB_SCHEMA', 'public'),
],
'sqlsrv' => [
'driver' => 'sqlsrv',
'host' => env('DB_HOST', null),
'database' => env('DB_DATABASE', null),
'username' => env('DB_USERNAME', null),
'password' => env('DB_PASSWORD', null),
'port' => env('DB_PORT', null),
'prefix' => env('DB_PREFIX', null),
'driver' => 'sqlsrv',
'host' => env('DB_HOST', null),
'database' => env('DB_DATABASE', null),
'username' => env('DB_USERNAME', null),
'password' => env('DB_PASSWORD', null),
'port' => env('DB_PORT', null),
'prefix' => env('DB_PREFIX', null),
],
],
@@ -131,6 +131,7 @@ return [
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DATABASE', 0),
'password' => env('REDIS_PASSWORD', null),
],
],

View File

@@ -49,6 +49,7 @@ return [
'displayers' => [
'CachetHQ\Cachet\Foundation\Exceptions\Displayers\JsonValidationDisplayer',
'CachetHQ\Cachet\Foundation\Exceptions\Displayers\RedirectDisplayer',
'CachetHQ\Cachet\Foundation\Exceptions\Displayers\ThrottleDisplayer',
'GrahamCampbell\Exceptions\Displayers\DebugDisplayer',
'GrahamCampbell\Exceptions\Displayers\HtmlDisplayer',
'GrahamCampbell\Exceptions\Displayers\JsonDisplayer',

64
config/langs.php Executable file → Normal file
View File

@@ -11,35 +11,35 @@
return [
// Enabled langs
'af' => [
'af' => [
'name' => 'Afrikaans',
'subset' => 'latin',
],
'ar' => [
'ar' => [
'name' => 'Arabic',
'subset' => 'latin',
],
'ca' => [
'ca' => [
'name' => 'Catalan',
'subset' => 'latin',
],
'cs' => [
'cs' => [
'name' => 'Czech',
'subset' => 'latin,latin-ext',
],
'da' => [
'da' => [
'name' => 'Danish',
'subset' => 'latin,latin-ext',
],
'de' => [
'de' => [
'name' => 'Deutsch',
'subset' => 'latin,latin-ext',
],
'el' => [
'el' => [
'name' => 'Greek',
'subset' => 'greek,greek-ext',
],
'en' => [
'en' => [
'name' => 'English',
'subset' => 'latin',
],
@@ -47,79 +47,79 @@ return [
'name' => 'CrowdIn - InContext Localization',
'subset' => 'latin',
],
'es' => [
'es' => [
'name' => 'Español',
'subset' => 'latin,latin-ext',
],
'fa' => [
'fa' => [
'name' => 'Persian',
'subset' => 'latin',
],
'fi' => [
'fi' => [
'name' => 'Finnish',
'subset' => 'latin,latin-ext',
],
'fr' => [
'fr' => [
'name' => 'Français',
'subset' => 'latin,latin-ext',
],
'he' => [
'he' => [
'name' => 'Hebrew',
'subset' => 'latin',
],
'hu' => [
'hu' => [
'name' => 'Hungarian',
'subset' => 'latin,latin-ext',
],
'id' => [
'id' => [
'name' => 'Indonesian',
'subset' => 'latin',
],
'it' => [
'it' => [
'name' => 'Italiano',
'subset' => 'latin,latin-ext',
],
'ja' => [
'ja' => [
'name' => 'Japanese',
'subset' => 'latin',
],
'ko' => [
'ko' => [
'name' => '한글',
'subset' => 'latin',
],
'nl' => [
'nl' => [
'name' => 'Nederlands',
'subset' => 'latin,latin-ext',
],
'no' => [
'no' => [
'name' => 'Norwegian',
'subset' => 'latin,latin-ext',
],
'pl' => [
'pl' => [
'name' => 'Polski',
'subset' => 'latin,latin-ext',
],
'pt-BR' => [
'name' => 'Portuguese',
'subset' => 'latin,latin-ext',
],
'pt-PT' => [
'name' => 'Portuguese, Brazilian',
'subset' => 'latin,latin-ext',
],
'ro' => [
'pt-PT' => [
'name' => 'Portuguese, Portugal',
'subset' => 'latin,latin-ext',
],
'ro' => [
'name' => 'Romanian',
'subset' => 'latin,latin-ext',
],
'ru' => [
'ru' => [
'name' => 'Русский',
'subset' => 'latin,cyrillic',
],
'sq' => [
'sq' => [
'name' => 'Albanian',
'subset' => 'latin,latin-ext',
],
'sr' => [
'sr' => [
'name' => 'Sebrian (Cyrillic)',
'subset' => 'latin,cyrillic,cyrillic-ext',
],
@@ -127,15 +127,15 @@ return [
'name' => 'Swedish',
'subset' => 'latin,latin-ext',
],
'tr' => [
'tr' => [
'name' => 'Turkish',
'subset' => 'latin,latin-ext',
],
'uk' => [
'uk' => [
'name' => 'Ukranian',
'subset' => 'latin,cyrillic-ext',
],
'vi' => [
'vi' => [
'name' => 'Vietnamese',
'subset' => 'latin,vietnamese',
],

View File

@@ -28,22 +28,22 @@ return [
],
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'domain' => env('MAIL_USERNAME'),
'secret' => env('MAIL_PASSWORD'),
],
'mandrill' => [
'secret' => env('MANDRILL_SECRET'),
'secret' => env('MAIL_PASSWORD'),
],
'ses' => [
'key' => env('SES_KEY'),
'secret' => env('SES_SECRET'),
'key' => env('MAIL_USERNAME'),
'secret' => env('MAIL_PASSWORD'),
'region' => 'us-east-1',
],
'sparkpost' => [
'secret' => env('SPARKPOST_SECRET'),
'secret' => env('MAIL_PASSWORD'),
],
];

59
config/setting.php Normal file
View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
/*
|--------------------------------------------------------------------------
| Dashboard Login Link
|--------------------------------------------------------------------------
|
| Whether to show the dashboard link in the footer by default.
|
*/
'dashboard_login_link' => true,
/*
|--------------------------------------------------------------------------
| Enable subscribers
|--------------------------------------------------------------------------
|
| Whether to allow people to subscribe to the status page.
|
*/
'enable_subscribers' => true,
/*
|--------------------------------------------------------------------------
| Automatic Localization
|--------------------------------------------------------------------------
|
| Whether to automatically localize the status to the visitors default
| browser language settings.
|
*/
'automatic_localization' => false,
/*
|--------------------------------------------------------------------------
| Show Support for Cachet
|--------------------------------------------------------------------------
|
| Whether to show the "Powered by Cachet" text in the footer.
|
*/
'show_support' => true,
];

View File

@@ -33,6 +33,7 @@ return [
'103.31.4.0/22',
'104.16.0.0/12',
'108.162.192.0/18',
'131.0.72.0/22',
'141.101.64.0/18',
'162.158.0.0/15',
'172.64.0.0/13',

1
database/backups/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*

View File

@@ -61,8 +61,10 @@ $factory->define(Metric::class, function ($faker) {
'suffix' => $faker->word(),
'description' => $faker->paragraph(),
'default_value' => 1,
'places' => 2,
'calc_type' => $faker->boolean(),
'display_chart' => $faker->boolean(),
'threshold' => 5,
];
});
@@ -70,6 +72,7 @@ $factory->define(MetricPoint::class, function ($faker) {
return [
'metric_id' => factory(Metric::class)->create()->id,
'value' => random_int(1, 100),
'counter' => 1,
];
});

View File

@@ -11,6 +11,7 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateJobsTable extends Migration
{

Some files were not shown because too many files have changed in this diff Show More