Compare commits

..

310 Commits
2.2 ... v2.3.6

Author SHA1 Message Date
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
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
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
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
400 changed files with 7419 additions and 4070 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

@@ -9,13 +9,6 @@ php:
sudo: false
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

@@ -2,15 +2,18 @@
[![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)
[![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.
## Supporting Cachet
Cachet is a BSD-3-licensed open source project. If you'd like to support future development, check out the [Cachet Patreon campaign](https://patreon.com/jbrooksuk).
## Features
- List your services components
@@ -52,10 +55,9 @@ 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. Modify 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!
@@ -66,7 +68,6 @@ Built using [Laravel](https://laravel.com), Cachet is very easy to jump into. Ha
These extra dependencies are required to develop Cachet:
- Node.js
- Bower
- Gulp
- Git
@@ -106,11 +107,3 @@ If you discover a security vulnerability within Cachet, please send an e-mail to
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)

View File

@@ -1 +1 @@
2.2.5-dev
2.3.6

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,35 +32,34 @@ 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,
]);
}
$subscriptions = Component::whereIn('id', $subscriptions);
} else {
$subscriptions = Component::all();
}
if ($subscriber->is_verified === false) {
if ($command->verified) {
dispatch(new VerifySubscriberCommand($subscriber));
} else {
event(new SubscriberHasSubscribedEvent($subscriber));
}
foreach ($subscriptions as $component) {
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));
}
return $subscriber;

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

@@ -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

@@ -46,7 +46,9 @@ class StatusPageComposer
];
} 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();
$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)) {

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,27 @@ class ConfigServiceProvider extends ServiceProvider
*/
public function boot()
{
$env = $this->app->environment();
$cli = $this->app->runningInConsole();
$repo = $this->app->make(Repository::class);
$cache = $this->app->make(Cache::class);
$loaded = $cli ? false : $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 ($cli === false && $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 +63,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 +94,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

@@ -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,13 @@
namespace CachetHQ\Cachet\Http\Controllers\Api;
use CachetHQ\Cachet\Integrations\Releases;
/**
* This is the general api controller.
*
* @author James Brooks <james@bluebaytravel.co.uk>
*/
class GeneralController extends AbstractApiController
{
/**
@@ -30,6 +37,11 @@ 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);
}
}

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,
null,
null,
Binput::get('status'),
null,
null,
null,
null
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $component;
return $this->item($component);
}
/**
@@ -51,11 +63,23 @@ 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 {
dispatch(new UpdateComponentCommand(
Component::find($componentId),
null,
null,
null,
null,
$order + 1,
null,
null
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
}
return $componentData;
return $this->collection(Component::query()->orderBy('order')->get());
}
/**
@@ -68,10 +92,15 @@ class ApiController extends Controller
$groupData = Binput::get('ids');
foreach ($groupData as $order => $groupId) {
ComponentGroup::find($groupId)->update(['order' => $order + 1]);
dispatch(new UpdateComponentGroupCommand(
ComponentGroup::find($groupId),
null,
$order + 1,
null
));
}
return $groupData;
return $this->collection(ComponentGroup::query()->orderBy('order')->get());
}
/**
@@ -91,20 +120,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;
@@ -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,47 @@ 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) {
dd($e->getMessageBag());
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

@@ -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,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,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\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!');
$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!');
}
}

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,29 +20,33 @@
],
"require": {
"php": ">=5.5.9",
"laravel/framework": "5.2.37",
"alt-three/badger": "^3.0",
"alt-three/bus": "^1.0",
"alt-three/emoji": "^3.0",
"alt-three/validator": "^1.4",
"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": "^1.1",
"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.3"
"roumen/feed": "^2.10.4"
},
"require-dev": {
"alt-three/testbench": "^1.1",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.5",
"alt-three/testbench": "^1.4",
"filp/whoops": "^2.1",
"fzaninotto/faker": "^1.6",
"graham-campbell/testbench-core": "^1.1",
"mockery/mockery": "0.9.5",
"phpunit/phpunit": "4.8.21",
@@ -54,7 +58,7 @@
"database"
],
"files": [
"app/Http/helpers.php"
"app/helpers.php"
],
"psr-4": {
"CachetHQ\\Cachet\\": "app/"
@@ -94,7 +98,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
"dev-master": "2.3-dev"
}
},
"minimum-stability": "dev",

815
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,9 @@ 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\EventServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\GitHubServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\RepositoryServiceProvider',
'CachetHQ\Cachet\Foundation\Providers\RouteServiceProvider',

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

@@ -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',

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

@@ -100,11 +100,11 @@ return [
'subset' => 'latin,latin-ext',
],
'pt-BR' => [
'name' => 'Portuguese',
'name' => 'Portuguese, Brazilian',
'subset' => 'latin,latin-ext',
],
'pt-PT' => [
'name' => 'Portuguese, Brazilian',
'name' => 'Portuguese, Portugal',
'subset' => 'latin,latin-ext',
],
'ro' => [

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

@@ -0,0 +1,49 @@
<?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.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AlterTableMetricPointsAddCounterColumn extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('metrics', function (Blueprint $table) {
$table->integer('threshold')->unsigned()->default(5)->after('default_view');
});
Schema::table('metric_points', function (Blueprint $table) {
$table->integer('counter')->unsigned()->default(1)->after('value');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('metrics', function (Blueprint $table) {
$table->dropColumn('threshold');
});
Schema::table('metric_points', function (Blueprint $table) {
$table->dropColumn('counter');
});
}
}

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.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class AlterTableSubscribersAddGlobalColumn extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('subscribers', function (Blueprint $table) {
$table->boolean('global')->after('verified_at')->default(1);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('subscribers', function (Blueprint $table) {
$table->dropColumn('global');
});
}
}

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.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AlterTableMetricsAddOrderColumn extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('metrics', function (Blueprint $table) {
$table->tinyInteger('order')->after('threshold')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('metrics', function (Blueprint $table) {
$table->dropColumn('order');
});
}
}

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.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCacheTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->unique();
$table->text('value');
$table->integer('expiration');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('cache');
}
}

View File

@@ -7,30 +7,30 @@ elixir(function (mix) {
mix
.sass('app.scss', 'public/dist/css/app.css')
.styles([
'vendor/bower_components/jquery-minicolors/jquery.minicolors.css',
'vendor/bower_components/sweetalert/dist/sweetalert.css',
'vendor/bower_components/github-markdown-css/github-markdown.css',
'node_modules/jquery-minicolors/jquery.minicolors.css',
'node_modules/sweetalert/dist/sweetalert.css',
'node_modules/github-markdown-css/github-markdown.css',
'public/dist/css/app.css'
], 'public/dist/css/all.css', './')
.scripts([
'vendor/bower_components/jquery/dist/jquery.js',
'vendor/bower_components/bootstrap-sass/assets/javascripts/bootstrap.js',
'vendor/bower_components/moment/min/moment-with-locales.js',
'vendor/bower_components/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js',
'vendor/bower_components/lodash/lodash.js',
'vendor/bower_components/autosize/dist/autosize.js',
'vendor/bower_components/messenger/build/js/messenger.js',
'vendor/bower_components/Sortable/Sortable.js',
'vendor/bower_components/livestampjs/livestamp.js',
'vendor/bower_components/jquery-minicolors/jquery.minicolors.js',
'vendor/bower_components/jquery-serialize-object/jquery.serialize-object.js',
'vendor/bower_components/chartjs/dist/Chart.js',
'vendor/bower_components/jquery-sparkline/dist/jquery.sparkline.js',
'vendor/bower_components/sweetalert/dist/sweetalert.min.js',
'node_modules/jquery/dist/jquery.js',
'node_modules/bootstrap-sass/assets/javascripts/bootstrap.js',
'node_modules/moment/min/moment-with-locales.js',
'node_modules/eonasdan-bootstrap-datetimepicker/src/js/bootstrap-datetimepicker.js',
'node_modules/lodash/lodash.js',
'node_modules/autosize/dist/autosize.js',
'node_modules/messenger/build/js/messenger.js',
'node_modules/sortablejs/Sortable.js',
'node_modules/livestamp/livestamp.js',
'node_modules/jquery-minicolors/jquery.minicolors.js',
'node_modules/jquery-serializeobject/jquery.serializeObject.js',
'node_modules/chart.js/dist/Chart.js',
'node_modules/jquery-sparkline/jquery.sparkline.js',
'node_modules/sweetalert/dist/sweetalert.min.js',
'resources/assets/js/password-strength.js',
'resources/assets/js/app.js',
'resources/assets/js/**/*.js'
], 'public/dist/js/all.js', './')
.version(['public/dist/css/all.css', 'public/dist/js/all.js'])
.copy('vendor/bower_components/ionicons/fonts/', 'public/fonts/');
.copy('node_modules/ionicons/fonts/', 'public/fonts/');
});

View File

@@ -1,8 +1,25 @@
{
"dependencies": {
"bower": "~1.7.7",
"gulp": "~3.9.1",
"laravel-elixir": "~5.0.0"
},
"private": true
"name": "cachet",
"devDependencies": {
"gulp": "~3.9.1",
"laravel-elixir": "~6.0.0-9",
"animate-sass": "git+https://github.com/tgdev/animate-sass.git",
"autosize": "^3.0.15",
"bootstrap-sass": "^3.3.6",
"chart.js": "^2.1.2",
"eonasdan-bootstrap-datetimepicker": "~3.1",
"github-markdown-css": "^2.3.0",
"ionicons": "~2.0",
"jquery-minicolors": "^2.1.10",
"jquery-serializeobject": "^1.0.0",
"jquery-sparkline": "^2.3.2",
"jquery": "~2.2",
"livestamp": "git+https://github.com/mattbradley/livestampjs.git#develop",
"lodash": "^4.12.0",
"messenger": "git+https://github.com/HubSpot/messenger.git",
"moment": "^2.13.0",
"sortablejs": "^1.4.2",
"sweetalert": "^1.1.3"
},
"private": true
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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