mirror of
https://github.com/cachethq/cachet.git
synced 2026-03-06 17:27:01 +00:00
Compare commits
972 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ed480dd64 | ||
|
|
c040fc334c | ||
|
|
48fdf79a8f | ||
|
|
fe3675ff33 | ||
|
|
94b4fe2d39 | ||
|
|
23cd590815 | ||
|
|
748962b1c6 | ||
|
|
6f0ccb7e76 | ||
|
|
0c637cc683 | ||
|
|
ec26e01e8d | ||
|
|
9caecd3268 | ||
|
|
f7ab99483c | ||
|
|
b9c9d28b59 | ||
|
|
1d32adb0e6 | ||
|
|
e36abd5ce4 | ||
|
|
735c722918 | ||
|
|
a588cfbd0a | ||
|
|
8f0e56873e | ||
|
|
dfcf9bd392 | ||
|
|
52c93a0dfe | ||
|
|
28a896bf02 | ||
|
|
06faecf3a7 | ||
|
|
5dfeaa3b6a | ||
|
|
18f5c29a16 | ||
|
|
3681d277a4 | ||
|
|
6752596ba8 | ||
|
|
d02ec7f150 | ||
|
|
300961cad4 | ||
|
|
12b6888feb | ||
|
|
dde38b08d3 | ||
|
|
0bdf3f35aa | ||
|
|
1a053ff5b8 | ||
|
|
7f54029bad | ||
|
|
f50a2c717e | ||
|
|
a4ab01d9dc | ||
|
|
dfa1650936 | ||
|
|
b3f4eb261a | ||
|
|
552c029682 | ||
|
|
ce833c52bc | ||
|
|
61acfd5dab | ||
|
|
72f6b7185d | ||
|
|
39e670edb5 | ||
|
|
0f99a14bd6 | ||
|
|
18cccd9a76 | ||
|
|
f15709850d | ||
|
|
a24604b169 | ||
|
|
5cb438a30b | ||
|
|
b43974eb48 | ||
|
|
c020fd50b7 | ||
|
|
b76acbeee1 | ||
|
|
f46371bde0 | ||
|
|
5853b43da1 | ||
|
|
d2f3572ffc | ||
|
|
8b98141c61 | ||
|
|
59ef9023d5 | ||
|
|
1c371e9fe3 | ||
|
|
cb518d0d14 | ||
|
|
64dd4cbf9c | ||
|
|
c0513da918 | ||
|
|
393669beae | ||
|
|
1dfc3683ce | ||
|
|
99d95a8e11 | ||
|
|
48e8bc84f8 | ||
|
|
3e065f6c9c | ||
|
|
249de039f6 | ||
|
|
cba2eece5f | ||
|
|
fbf141c39d | ||
|
|
b639d1e5ab | ||
|
|
168e36f224 | ||
|
|
87b5b67be4 | ||
|
|
f79eef0e88 | ||
|
|
30978bd125 | ||
|
|
22b6135541 | ||
|
|
cc2f4de125 | ||
|
|
baea4e9a03 | ||
|
|
1bc48e0c60 | ||
|
|
0d5cfb17d4 | ||
|
|
429719585f | ||
|
|
63a2d9445b | ||
|
|
967961787c | ||
|
|
e281597260 | ||
|
|
c6b4d9ed5e | ||
|
|
31eb7086a2 | ||
|
|
ae874c704e | ||
|
|
7b2b90a78d | ||
|
|
1d6b117a55 | ||
|
|
7a08cfd00a | ||
|
|
52fda22acc | ||
|
|
9531a909d4 | ||
|
|
5fcc0ce2f6 | ||
|
|
843eabbcc3 | ||
|
|
16f057500b | ||
|
|
e477de77be | ||
|
|
e7066864e2 | ||
|
|
16932219a7 | ||
|
|
512e1a6661 | ||
|
|
0e5bb00f44 | ||
|
|
6aed3531dd | ||
|
|
6e79575a1c | ||
|
|
61a371520d | ||
|
|
1f46e06e80 | ||
|
|
0e82925f95 | ||
|
|
c3cfeeb14c | ||
|
|
7738c73860 | ||
|
|
d8eb86e4d2 | ||
|
|
48ab04c425 | ||
|
|
035164fae6 | ||
|
|
d4bcdf0e29 | ||
|
|
f41aa311c5 | ||
|
|
ef7236c16b | ||
|
|
84915404b9 | ||
|
|
6dfadc2f94 | ||
|
|
7f6effff96 | ||
|
|
63589167f3 | ||
|
|
9e96f28ddf | ||
|
|
6d920b2111 | ||
|
|
760a034c0c | ||
|
|
0a9d1b313a | ||
|
|
e9020d3e08 | ||
|
|
d61c9d190f | ||
|
|
520785b5ca | ||
|
|
dd0052a50f | ||
|
|
cd48270cad | ||
|
|
79b837d65f | ||
|
|
5f639b718f | ||
|
|
eb18e7d982 | ||
|
|
cd70023bbe | ||
|
|
4277ec77e4 | ||
|
|
1f1cf5a242 | ||
|
|
495f118116 | ||
|
|
171588c3a5 | ||
|
|
72def0eb60 | ||
|
|
79a8026191 | ||
|
|
11bb77b944 | ||
|
|
3f932a2ec2 | ||
|
|
9f0a11eeb4 | ||
|
|
34f5775bd8 | ||
|
|
2a4a7d72a2 | ||
|
|
2dd7fba3e1 | ||
|
|
ac91ba5d67 | ||
|
|
2a04e8907c | ||
|
|
c66f66a6f4 | ||
|
|
589f82af84 | ||
|
|
ef91bacc35 | ||
|
|
84f1f2fe0b | ||
|
|
525563c3c0 | ||
|
|
4b8bc99b5e | ||
|
|
66a300cade | ||
|
|
68f80c2f95 | ||
|
|
a087b2a21a | ||
|
|
f155bfa419 | ||
|
|
255922804c | ||
|
|
7191bdacf8 | ||
|
|
491b07d498 | ||
|
|
467453c29d | ||
|
|
8d42f0f97b | ||
|
|
fc170bea0a | ||
|
|
d455f56243 | ||
|
|
cef1d82d8c | ||
|
|
e7caa3ca37 | ||
|
|
2a320477e3 | ||
|
|
1c593bb2f1 | ||
|
|
e557e5eb9b | ||
|
|
33ed227c97 | ||
|
|
f4dc5d09ab | ||
|
|
d86d2e8201 | ||
|
|
9ac9b5898e | ||
|
|
1eea552d6e | ||
|
|
3fd9bfe00d | ||
|
|
282ee9802d | ||
|
|
ce7104d679 | ||
|
|
bcdaec1d3a | ||
|
|
53e1837368 | ||
|
|
b8324c3e7c | ||
|
|
7ea832de8e | ||
|
|
368045b418 | ||
|
|
0bfaeaa4fc | ||
|
|
3f9ba0ee97 | ||
|
|
045bbeeba0 | ||
|
|
5cce6d4184 | ||
|
|
9f1dd6fedc | ||
|
|
ddb2972a4a | ||
|
|
ba84e3ee23 | ||
|
|
400c94d06d | ||
|
|
ebe31e2070 | ||
|
|
7b29049dd7 | ||
|
|
ee73222638 | ||
|
|
6f1cba91df | ||
|
|
b313c45cd2 | ||
|
|
3e35d29d8f | ||
|
|
8ffa6c7996 | ||
|
|
07750e9365 | ||
|
|
63a79d9d19 | ||
|
|
12efe8113e | ||
|
|
d8c6be3f9e | ||
|
|
8bffaa5c9a | ||
|
|
6bf9221d74 | ||
|
|
fa5875da76 | ||
|
|
34a36bc6c1 | ||
|
|
f8a35d71b4 | ||
|
|
8a8edea07a | ||
|
|
e3973414cd | ||
|
|
9bb670cb95 | ||
|
|
127996f08f | ||
|
|
a6768da7bb | ||
|
|
123f05bd84 | ||
|
|
ecc91abcae | ||
|
|
bb9def5821 | ||
|
|
11cbbb42fb | ||
|
|
b361618ec0 | ||
|
|
ee288e0505 | ||
|
|
7c5e214b1c | ||
|
|
0e1d52b3bc | ||
|
|
77d576fd39 | ||
|
|
73fb33d03e | ||
|
|
2746001cdb | ||
|
|
036da9248d | ||
|
|
2d72914205 | ||
|
|
237ae7dcdc | ||
|
|
8408b92d0c | ||
|
|
2da10d1694 | ||
|
|
7ac2987809 | ||
|
|
5f1c3c92c4 | ||
|
|
48f68994ba | ||
|
|
adfa39f336 | ||
|
|
357389908f | ||
|
|
a2252707af | ||
|
|
5057dee219 | ||
|
|
430a80535c | ||
|
|
5c815756fe | ||
|
|
b1547b9d74 | ||
|
|
42a2825a70 | ||
|
|
3dd7d9b41d | ||
|
|
7552177585 | ||
|
|
6120d9627e | ||
|
|
22a7523347 | ||
|
|
b4c052c5ad | ||
|
|
0af0e01d31 | ||
|
|
04ca418b32 | ||
|
|
8ca56c0a47 | ||
|
|
dccca87e2e | ||
|
|
63b15530f0 | ||
|
|
008b7ce3e3 | ||
|
|
e173c616fc | ||
|
|
63e629df99 | ||
|
|
c930ecc568 | ||
|
|
c542fe762c | ||
|
|
5a6dfce84e | ||
|
|
1fc399085c | ||
|
|
dfced9e81c | ||
|
|
e6412e9be5 | ||
|
|
6c67e01297 | ||
|
|
e4a2ef3df7 | ||
|
|
560be25f99 | ||
|
|
61a71166b3 | ||
|
|
359cd934c0 | ||
|
|
4b45eed503 | ||
|
|
73a14d5980 | ||
|
|
c614bd8e2a | ||
|
|
bd26724f4a | ||
|
|
d7650d1815 | ||
|
|
9975ecab54 | ||
|
|
a508a326ca | ||
|
|
5d836619c6 | ||
|
|
4ff927968b | ||
|
|
4a9e2be6ee | ||
|
|
3f92f85cda | ||
|
|
6138a9c4ef | ||
|
|
210ac14e73 | ||
|
|
e5b17f1ce7 | ||
|
|
3a31fb01d6 | ||
|
|
ff97806361 | ||
|
|
b12fdb61cb | ||
|
|
75a12767e2 | ||
|
|
b4df0d7aae | ||
|
|
b4a618f362 | ||
|
|
0af3fbc86c | ||
|
|
8ed5ea3174 | ||
|
|
6f98fefb85 | ||
|
|
4b0a1da1b9 | ||
|
|
89779d9e3c | ||
|
|
8b4ab5f09b | ||
|
|
c077c692ed | ||
|
|
dea9ebbfc9 | ||
|
|
0542cda408 | ||
|
|
6041431d12 | ||
|
|
6703854224 | ||
|
|
aa8618c163 | ||
|
|
c040a42ee7 | ||
|
|
983e1a781f | ||
|
|
b5256a9558 | ||
|
|
6058cad6e0 | ||
|
|
cb42bde85c | ||
|
|
e9104d6182 | ||
|
|
1153c0da76 | ||
|
|
4e1b1fe4ef | ||
|
|
0078f1dd1e | ||
|
|
6ed0d2d4c2 | ||
|
|
4b9a5e8e69 | ||
|
|
375c831391 | ||
|
|
126c088863 | ||
|
|
9f226eea9c | ||
|
|
4ad72633ea | ||
|
|
2b18410a99 | ||
|
|
2e353fc707 | ||
|
|
cb8d4282e4 | ||
|
|
d7fd04b183 | ||
|
|
3f6b825692 | ||
|
|
7e95648ff9 | ||
|
|
1dfd481d95 | ||
|
|
a8f04914fa | ||
|
|
1093d2169d | ||
|
|
4098bfe641 | ||
|
|
5c51806417 | ||
|
|
b616842ec0 | ||
|
|
6c693ea4d5 | ||
|
|
c6d9b62248 | ||
|
|
2770036621 | ||
|
|
54943af362 | ||
|
|
00eba028e5 | ||
|
|
421d8c5d05 | ||
|
|
c28e86c3fa | ||
|
|
a6706b7f3f | ||
|
|
d2fae69bf2 | ||
|
|
9abe4962a9 | ||
|
|
e009ae4754 | ||
|
|
4213a9a2ca | ||
|
|
5da83aed92 | ||
|
|
a159600919 | ||
|
|
1b0e583a8a | ||
|
|
1c496338cd | ||
|
|
f7353c0c64 | ||
|
|
090f4167f2 | ||
|
|
3fbf056056 | ||
|
|
3a554d25ba | ||
|
|
dbd0fd57f2 | ||
|
|
226596d47b | ||
|
|
fa8f957631 | ||
|
|
e465295e48 | ||
|
|
94b5ad7b09 | ||
|
|
9512346c0c | ||
|
|
985119e125 | ||
|
|
11b5df1499 | ||
|
|
76aa95389f | ||
|
|
f346449714 | ||
|
|
a2da594586 | ||
|
|
6b3bb85d53 | ||
|
|
5d28ea4e69 | ||
|
|
f724248b1f | ||
|
|
a70ecd9e6d | ||
|
|
828ea1c2d3 | ||
|
|
c1f65347e5 | ||
|
|
61abbcc367 | ||
|
|
4bf980bc1a | ||
|
|
b88354d261 | ||
|
|
5fb88b1936 | ||
|
|
e0b2368342 | ||
|
|
fc87437edc | ||
|
|
7786f54ad3 | ||
|
|
ee0d56d22b | ||
|
|
51eb5917b6 | ||
|
|
6433c48a5f | ||
|
|
d3af0d37be | ||
|
|
0430086750 | ||
|
|
e43516460b | ||
|
|
036787f3e3 | ||
|
|
b20272d14a | ||
|
|
5c11c1ff72 | ||
|
|
5bde0dddff | ||
|
|
beee77d2e3 | ||
|
|
4c6106e3a6 | ||
|
|
c4e7010cfc | ||
|
|
0b505b31c6 | ||
|
|
87b31b2b54 | ||
|
|
c0da527878 | ||
|
|
539deaaea8 | ||
|
|
c04a845c54 | ||
|
|
09347fc120 | ||
|
|
3c2ec3dbe6 | ||
|
|
843f69b9e1 | ||
|
|
5c54286fb1 | ||
|
|
32fd6535c1 | ||
|
|
622a103b7b | ||
|
|
b8a3614649 | ||
|
|
98ac8383c9 | ||
|
|
747c94ebd7 | ||
|
|
4c1a7abb7a | ||
|
|
0a5691a4c2 | ||
|
|
9aa16286a5 | ||
|
|
d4a693f8fb | ||
|
|
0ea5990af6 | ||
|
|
22cdc45074 | ||
|
|
61ea95a403 | ||
|
|
0dcf3f958f | ||
|
|
b672f42635 | ||
|
|
7f6c7f7a71 | ||
|
|
54ed6eda60 | ||
|
|
877b25cfbb | ||
|
|
8048c02286 | ||
|
|
082367793c | ||
|
|
518d28f07b | ||
|
|
a01ccbf953 | ||
|
|
a69c72ef40 | ||
|
|
87b51835cc | ||
|
|
edecc98cbf | ||
|
|
a1a78df602 | ||
|
|
2b5127505f | ||
|
|
cd029e0a79 | ||
|
|
4a0e12645c | ||
|
|
18c12cf9b0 | ||
|
|
a2b6141b55 | ||
|
|
3305628b16 | ||
|
|
81b6fd95e9 | ||
|
|
cf2a9454c8 | ||
|
|
d220823d8e | ||
|
|
81d4cf89e6 | ||
|
|
e0a5e4df9b | ||
|
|
090a80992a | ||
|
|
a659db17ae | ||
|
|
67d8a1ca35 | ||
|
|
220d1a1fe5 | ||
|
|
d8bc1cd517 | ||
|
|
e7768a1cec | ||
|
|
4b1bd76ff6 | ||
|
|
37e778585a | ||
|
|
2646963a2e | ||
|
|
ebe162fe71 | ||
|
|
071159ffa1 | ||
|
|
b9549829c2 | ||
|
|
8c6be5006d | ||
|
|
cae3a54b50 | ||
|
|
0b3e29e8dd | ||
|
|
a5d690abdd | ||
|
|
31137c71d4 | ||
|
|
76963a186c | ||
|
|
3c2d85188b | ||
|
|
db407e81ea | ||
|
|
9dda8d5ae7 | ||
|
|
ad637fbb81 | ||
|
|
6805e75f63 | ||
|
|
bc5ae22165 | ||
|
|
6696465d45 | ||
|
|
58bb80efc9 | ||
|
|
e6b7b41f24 | ||
|
|
682ac8ac5d | ||
|
|
9687384709 | ||
|
|
347f443d4e | ||
|
|
548d9f8c3a | ||
|
|
eeb85c1815 | ||
|
|
1112c17306 | ||
|
|
aa421830bb | ||
|
|
99982566b8 | ||
|
|
115b5b7013 | ||
|
|
65a263a2dd | ||
|
|
98e0a7b7b4 | ||
|
|
7581d96c54 | ||
|
|
8e7c0f3f0d | ||
|
|
50c5b23f96 | ||
|
|
d77c53b5eb | ||
|
|
cda30429be | ||
|
|
8cfb1282d3 | ||
|
|
9faa4b77cd | ||
|
|
6a5c5776a9 | ||
|
|
c63a76c86d | ||
|
|
89718f5fbc | ||
|
|
8e84278a24 | ||
|
|
16470d548e | ||
|
|
320203262f | ||
|
|
ae3b2e44b5 | ||
|
|
755b8143cb | ||
|
|
4667948949 | ||
|
|
0f7bee9c61 | ||
|
|
10430ddad3 | ||
|
|
6e06e6a4dd | ||
|
|
393a1119da | ||
|
|
47fb5b2789 | ||
|
|
00caff7ecf | ||
|
|
e1fccf1b26 | ||
|
|
1a1e914d59 | ||
|
|
f2e995513f | ||
|
|
e780735987 | ||
|
|
1eaa4594af | ||
|
|
9d51a34bd4 | ||
|
|
d1681beae4 | ||
|
|
0254d3df00 | ||
|
|
2d15c27191 | ||
|
|
17f12a0cb7 | ||
|
|
e17400be21 | ||
|
|
62d7cb615d | ||
|
|
89759bb0ea | ||
|
|
7ee6cc91e7 | ||
|
|
bf2809bf50 | ||
|
|
fcbd5b68b2 | ||
|
|
1b1490f8bd | ||
|
|
019cafbbf5 | ||
|
|
a5a66e0de6 | ||
|
|
018aa8b0f0 | ||
|
|
abcfdcdd0e | ||
|
|
ceb95c30c4 | ||
|
|
08f9936e1d | ||
|
|
1647c4fc45 | ||
|
|
0eaf24febf | ||
|
|
544a6900bb | ||
|
|
b6cbf817ab | ||
|
|
1dab6c0f08 | ||
|
|
9dfd47b1e9 | ||
|
|
0f26675767 | ||
|
|
24f2bb7fd4 | ||
|
|
4be32a6112 | ||
|
|
0b23c86dd2 | ||
|
|
22a6e70cfd | ||
|
|
8e6569b63d | ||
|
|
2c8c13cd82 | ||
|
|
3aabf285a9 | ||
|
|
d07e58d3db | ||
|
|
0d88d80d48 | ||
|
|
ea638ef2ae | ||
|
|
b31d30dd10 | ||
|
|
a5494b6559 | ||
|
|
909a67fe93 | ||
|
|
2a8a4027d6 | ||
|
|
27bc034691 | ||
|
|
433093a012 | ||
|
|
fde7ad65f6 | ||
|
|
172228fdb3 | ||
|
|
d287b10bc1 | ||
|
|
db8fddf378 | ||
|
|
fc45fb60ec | ||
|
|
b11b5f2cf9 | ||
|
|
951250f1d1 | ||
|
|
c9bdbf38d8 | ||
|
|
7449d44bc8 | ||
|
|
db5ce4397b | ||
|
|
3256ad8660 | ||
|
|
486198872a | ||
|
|
de12ccf291 | ||
|
|
e1f5aa0639 | ||
|
|
a70db4c031 | ||
|
|
c1daf0ae0a | ||
|
|
fad9e5fd92 | ||
|
|
c224b5d05b | ||
|
|
78cc258d96 | ||
|
|
c87c37ad73 | ||
|
|
ab02399ca6 | ||
|
|
c3cf0b9e81 | ||
|
|
2e5ead7886 | ||
|
|
5a444d2c03 | ||
|
|
34cf7428df | ||
|
|
e3fa11d917 | ||
|
|
6153d08880 | ||
|
|
521c69b39e | ||
|
|
9caf55d92e | ||
|
|
357a1b4e86 | ||
|
|
c3ad824def | ||
|
|
abeb6fd917 | ||
|
|
be794d4ef2 | ||
|
|
fe8ff219ef | ||
|
|
e01993e5b8 | ||
|
|
6138ba99b8 | ||
|
|
6efe83c6b4 | ||
|
|
1b152eb0fe | ||
|
|
62012f4dab | ||
|
|
5c39be5b2c | ||
|
|
1e73861e15 | ||
|
|
4099a8f4e6 | ||
|
|
163f57397d | ||
|
|
494badb00a | ||
|
|
84bea58f99 | ||
|
|
d7ffbeef0c | ||
|
|
4aff0ddec0 | ||
|
|
749c83169c | ||
|
|
58a212c2e4 | ||
|
|
0c72e5eec8 | ||
|
|
83bc743d79 | ||
|
|
35dcc8c928 | ||
|
|
93d1f8784a | ||
|
|
0450f2f330 | ||
|
|
595b152db5 | ||
|
|
cc15d078bf | ||
|
|
3b568bbc07 | ||
|
|
ae4c414a82 | ||
|
|
5358cf5e1e | ||
|
|
6dedacad57 | ||
|
|
29c9211935 | ||
|
|
7d47546650 | ||
|
|
f5a9941903 | ||
|
|
52b7bf74b9 | ||
|
|
6a59d5ac4d | ||
|
|
19b7b0c3b2 | ||
|
|
d4507e0722 | ||
|
|
47bc73f9e9 | ||
|
|
81fc6479d5 | ||
|
|
c9ca99990c | ||
|
|
e0d35d6e64 | ||
|
|
813be34f25 | ||
|
|
900723105b | ||
|
|
8012a201b9 | ||
|
|
706e7765e1 | ||
|
|
bcff3d0730 | ||
|
|
db5bf005bc | ||
|
|
dac88efce6 | ||
|
|
38ef2630ee | ||
|
|
c215ad4111 | ||
|
|
c899839a7f | ||
|
|
2e5e8b1545 | ||
|
|
7e556d5dba | ||
|
|
331e176fbc | ||
|
|
bde0abe472 | ||
|
|
f2bf2eff68 | ||
|
|
50ab96fc4f | ||
|
|
3cbee1302e | ||
|
|
8d74923e3f | ||
|
|
dbf6fe528c | ||
|
|
2a9d1b4897 | ||
|
|
c2192d7ef7 | ||
|
|
1b580360ac | ||
|
|
15407ad9a3 | ||
|
|
14127de3ca | ||
|
|
57988f0009 | ||
|
|
7ac7344b5e | ||
|
|
472dcfe85a | ||
|
|
77409abc5a | ||
|
|
7548ae7e58 | ||
|
|
074d3457c6 | ||
|
|
205748b182 | ||
|
|
1f4bee67d6 | ||
|
|
2f0df5ab7b | ||
|
|
8fe75f7112 | ||
|
|
b3ee389ca5 | ||
|
|
a40bfd12b7 | ||
|
|
8d86c5e072 | ||
|
|
20ca9ceade | ||
|
|
6c22c52e3a | ||
|
|
ecfa9b1f01 | ||
|
|
380048dd53 | ||
|
|
ac57936b62 | ||
|
|
962b008fb2 | ||
|
|
5b1dd8ad00 | ||
|
|
c1d8203602 | ||
|
|
dc73f62803 | ||
|
|
fdbde63143 | ||
|
|
9a9ec833eb | ||
|
|
136cce2d6d | ||
|
|
880a2d9cd9 | ||
|
|
aea18a51c2 | ||
|
|
84bedf5c60 | ||
|
|
309a3f96ef | ||
|
|
acd1ac496d | ||
|
|
8c273b8579 | ||
|
|
b30a774bed | ||
|
|
3ea1428c23 | ||
|
|
d6ab5efd14 | ||
|
|
a94be2efca | ||
|
|
f8505a866d | ||
|
|
27235abab2 | ||
|
|
f70bc7a42f | ||
|
|
6cf622ae21 | ||
|
|
dd1a14a438 | ||
|
|
387818dbb2 | ||
|
|
5d70d244ce | ||
|
|
ddc846d959 | ||
|
|
6fb6641499 | ||
|
|
d5eb087eca | ||
|
|
fd25edb2f0 | ||
|
|
5879a2cb1a | ||
|
|
df5b9b89e6 | ||
|
|
47a5569f02 | ||
|
|
ca4a72c518 | ||
|
|
6e45b5ae88 | ||
|
|
99dbc2cd6c | ||
|
|
2f2ed53b58 | ||
|
|
7e1ead91ed | ||
|
|
28578474b9 | ||
|
|
d4e332bf55 | ||
|
|
13f0f67a7b | ||
|
|
709cac9332 | ||
|
|
e17196d4c9 | ||
|
|
80be140c1f | ||
|
|
4f9af8bca0 | ||
|
|
af3b7836ff | ||
|
|
cc4a960ea6 | ||
|
|
467d29ca11 | ||
|
|
ac1355771c | ||
|
|
c41adc1019 | ||
|
|
5b74b2d625 | ||
|
|
d7835f68ed | ||
|
|
a1f1a2b969 | ||
|
|
77394995e4 | ||
|
|
9953557497 | ||
|
|
4dc175b2be | ||
|
|
4a2fe00c89 | ||
|
|
db55283b85 | ||
|
|
5c6440e890 | ||
|
|
6b299b0a90 | ||
|
|
9b5d4aa7c4 | ||
|
|
1fdda03199 | ||
|
|
7c846d06ff | ||
|
|
e34e4381d4 | ||
|
|
b255f71958 | ||
|
|
9560b2a0dc | ||
|
|
f8491ceead | ||
|
|
823fe6e9e2 | ||
|
|
b7e9c07a05 | ||
|
|
a9d83e1337 | ||
|
|
86cfcb501a | ||
|
|
8f68b2a347 | ||
|
|
1423a9bbd0 | ||
|
|
39d302175c | ||
|
|
fffb4d964c | ||
|
|
7ba12960dd | ||
|
|
440f45ed4a | ||
|
|
549eaa3382 | ||
|
|
5305f71c2b | ||
|
|
bf749c54ee | ||
|
|
f1e2c1ef68 | ||
|
|
4ab9807cbe | ||
|
|
9f9c9a10c5 | ||
|
|
a5ed3c40a9 | ||
|
|
1fe4789760 | ||
|
|
9c1e94c550 | ||
|
|
958c9eadc7 | ||
|
|
ca3f516457 | ||
|
|
04215fc37a | ||
|
|
8d90357f40 | ||
|
|
46add8d841 | ||
|
|
1e695d51b5 | ||
|
|
65f864d743 | ||
|
|
bc51aac622 | ||
|
|
3c39ccd5df | ||
|
|
cd4a96983e | ||
|
|
c0d3602a91 | ||
|
|
08175b89e9 | ||
|
|
ece696d7db | ||
|
|
e7052f5aff | ||
|
|
ff97327803 | ||
|
|
8dda99c499 | ||
|
|
947aa27e40 | ||
|
|
1425c15ee8 | ||
|
|
cdbd2db8e9 | ||
|
|
ef02d77439 | ||
|
|
88ce241785 | ||
|
|
b64dd1e87e | ||
|
|
a0b9856d61 | ||
|
|
d6552982a3 | ||
|
|
42eac92737 | ||
|
|
a2cee97f33 | ||
|
|
0541a662ff | ||
|
|
6c9b7a6fde | ||
|
|
ff9a2802f8 | ||
|
|
06ca0ffb21 | ||
|
|
80e5655be9 | ||
|
|
0b7ca97e53 | ||
|
|
812160839f | ||
|
|
27e46d77e6 | ||
|
|
a10d12e589 | ||
|
|
ac7652f3aa | ||
|
|
c63fedefe9 | ||
|
|
c17ef87651 | ||
|
|
846f77b054 | ||
|
|
d5c29a6c4b | ||
|
|
31861f020f | ||
|
|
abf83361e8 | ||
|
|
3e8801d8d4 | ||
|
|
bbc9eb1f81 | ||
|
|
c8b602d349 | ||
|
|
715eb02844 | ||
|
|
c8f7e92124 | ||
|
|
eae44ee6cb | ||
|
|
3fde593a86 | ||
|
|
1527ec8ddc | ||
|
|
c90584bda5 | ||
|
|
aea3b40c54 | ||
|
|
d394e108bd | ||
|
|
0df9f01ffe | ||
|
|
9745bb7543 | ||
|
|
cab030237b | ||
|
|
439ac9fe44 | ||
|
|
7eef9467aa | ||
|
|
15387b1da8 | ||
|
|
c33d297fa4 | ||
|
|
c8af103498 | ||
|
|
0e2610eee0 | ||
|
|
e201a6ed06 | ||
|
|
1e4d616b88 | ||
|
|
2de01671e9 | ||
|
|
dd26a3af34 | ||
|
|
4b41395144 | ||
|
|
2dacc71e8a | ||
|
|
4869c7ee21 | ||
|
|
9293dcf0df | ||
|
|
150057ef50 | ||
|
|
c81f18c3bc | ||
|
|
42aa437ac2 | ||
|
|
6a780314fb | ||
|
|
ffae9cf3d4 | ||
|
|
091f59c241 | ||
|
|
288fa6180b | ||
|
|
7ecb546a86 | ||
|
|
f7c8dd6254 | ||
|
|
2adc9d032a | ||
|
|
f53075ec4f | ||
|
|
0190813012 | ||
|
|
20d187d642 | ||
|
|
91bd9288f5 | ||
|
|
24df32e9a9 | ||
|
|
fb75ad6902 | ||
|
|
aa87274378 | ||
|
|
696a1126c7 | ||
|
|
7c4787a1a7 | ||
|
|
74d24b6809 | ||
|
|
22226c666f | ||
|
|
b3244a4639 | ||
|
|
6cf3c4c109 | ||
|
|
8f47bd4a4f | ||
|
|
c03f01ca44 | ||
|
|
ab0ed775e1 | ||
|
|
5a600c0e17 | ||
|
|
601a863fc4 | ||
|
|
366eab9cae | ||
|
|
021c2890d2 | ||
|
|
2eff325a23 | ||
|
|
d7e70c1870 | ||
|
|
0311d59e78 | ||
|
|
6a6c8d866e | ||
|
|
11d82a22c3 | ||
|
|
dcfe55e881 | ||
|
|
494d1a021c | ||
|
|
d95557d683 | ||
|
|
48ccc02799 | ||
|
|
f3440389be | ||
|
|
bad9b1d550 | ||
|
|
e1d07fdc99 | ||
|
|
e69395a5ad | ||
|
|
1c10cba5fc | ||
|
|
3a1042e6ae | ||
|
|
a2a008b108 | ||
|
|
11f8ba81fd | ||
|
|
e8d216b671 | ||
|
|
167d076edc | ||
|
|
5c5634d355 | ||
|
|
1eea34e832 | ||
|
|
3873fd90ba | ||
|
|
38a659b08c | ||
|
|
55b6961a4b | ||
|
|
2b87629dca | ||
|
|
48c4240c38 | ||
|
|
f84793bfab | ||
|
|
0200ac848f | ||
|
|
787bb74b3b | ||
|
|
8f58cdd182 | ||
|
|
8f0691f3bd | ||
|
|
a81ce21721 | ||
|
|
1e0992be6c | ||
|
|
00706ad744 | ||
|
|
31d47cfb35 | ||
|
|
7e29f7d363 | ||
|
|
71a1ab091f | ||
|
|
9e020e5d59 | ||
|
|
5e10f1c777 | ||
|
|
a2aa116204 | ||
|
|
ac65cc56bb | ||
|
|
9e3f07742b | ||
|
|
b120c37cbc | ||
|
|
6211e7a1f8 | ||
|
|
5d23f83db6 | ||
|
|
a2ea9bf10b | ||
|
|
d6495dec96 | ||
|
|
5cbe3ba708 | ||
|
|
375d2330de | ||
|
|
e39a3cde16 | ||
|
|
13d0ff320a | ||
|
|
72577a04b7 | ||
|
|
0bf2039e60 | ||
|
|
414efb0ce7 | ||
|
|
0500d63654 | ||
|
|
3a86862e4f | ||
|
|
8df65b2dc3 | ||
|
|
c3bc8fdd2d | ||
|
|
200cd62dc9 | ||
|
|
527e5872df | ||
|
|
ebaf4e9395 | ||
|
|
d0afcbffe4 | ||
|
|
2e9d56d053 | ||
|
|
90419e70cf | ||
|
|
0566133c12 | ||
|
|
8e824eb5e0 | ||
|
|
5534cdbdde | ||
|
|
2db0b011e6 | ||
|
|
962b40f0e9 | ||
|
|
96450c476c | ||
|
|
ba09b3f7cd | ||
|
|
053e3e9477 | ||
|
|
ff939e356e | ||
|
|
e4d5c53d69 | ||
|
|
4549c51959 | ||
|
|
20e4ae5ff8 | ||
|
|
77f455651d | ||
|
|
7a3c231f68 | ||
|
|
0ec77d7652 | ||
|
|
4f886e15ed | ||
|
|
d3454d8126 | ||
|
|
ccc0ebb7d5 | ||
|
|
f6bfb10500 | ||
|
|
e241f38dd7 | ||
|
|
fb0dd82fe2 | ||
|
|
6997e0c451 | ||
|
|
2e73d071c9 | ||
|
|
f86f6bbc08 | ||
|
|
6c872e60fc | ||
|
|
23f77a7e72 | ||
|
|
25dd45f47c | ||
|
|
2680bafd69 | ||
|
|
08c1f105c2 | ||
|
|
c152892610 | ||
|
|
d54ead2868 | ||
|
|
81ae63b2c0 | ||
|
|
9f17c34f50 | ||
|
|
d0be61ad96 | ||
|
|
89f99ef663 | ||
|
|
9f4270f4a5 | ||
|
|
bda3484cac | ||
|
|
aec2ef0198 | ||
|
|
651edcc7c5 | ||
|
|
d975cd8ca1 | ||
|
|
c315d04d5d | ||
|
|
e77562d469 | ||
|
|
088fd6631d | ||
|
|
cc102847f9 | ||
|
|
b88a102629 | ||
|
|
452eb653a5 | ||
|
|
1584883f39 | ||
|
|
595a720d8b | ||
|
|
2a0decc7c6 | ||
|
|
d10386821e | ||
|
|
8da8e83e48 | ||
|
|
80bb601813 | ||
|
|
21b60e0708 | ||
|
|
0fd88a6ba7 | ||
|
|
8da7bde2a5 | ||
|
|
ceb119b226 | ||
|
|
6790d85a87 | ||
|
|
873f61fde6 | ||
|
|
f716c7fd1d | ||
|
|
50ac69b234 | ||
|
|
712b1078f2 | ||
|
|
893e61e319 | ||
|
|
bfd0ccd652 | ||
|
|
3feb93c074 | ||
|
|
a5ce958b92 | ||
|
|
47e1dff5c5 | ||
|
|
9ddf9e3e1a | ||
|
|
9747cb9204 | ||
|
|
bd35db8d87 | ||
|
|
23ff795809 | ||
|
|
48d2721605 | ||
|
|
9d78092121 | ||
|
|
a0b1501b2a | ||
|
|
c4e724e0e6 | ||
|
|
792a8f6a07 | ||
|
|
684b480338 | ||
|
|
f1b61c7ef1 | ||
|
|
05bb91d2d9 | ||
|
|
5abd25c408 | ||
|
|
e1a8e2220d | ||
|
|
9d9d3b2e6c | ||
|
|
df373e93ef | ||
|
|
28e7af7107 | ||
|
|
f9bc46b460 | ||
|
|
3730ca8811 | ||
|
|
69716730ac | ||
|
|
28bf01396c | ||
|
|
a9185b7876 |
4
.bowerrc
4
.bowerrc
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"directory": "vendor/bower_components",
|
||||
"interactive": false
|
||||
}
|
||||
19
.github/ISSUE_TEMPLATE
vendored
19
.github/ISSUE_TEMPLATE
vendored
@@ -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
|
||||
|
||||
19
.travis.yml
19
.travis.yml
@@ -1,21 +1,18 @@
|
||||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
dist: trusty
|
||||
|
||||
php:
|
||||
- 5.5.9
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- hhvm
|
||||
- 7.1
|
||||
|
||||
sudo: false
|
||||
before_install: cp .env.example .env
|
||||
|
||||
install:
|
||||
- travis_retry composer install --no-interaction --no-scripts --prefer-source
|
||||
install: travis_retry composer install --no-interaction --no-scripts --prefer-source
|
||||
|
||||
script:
|
||||
- if [ "$TRAVIS_PHP_VERSION" != "5.6" ] || [ "$TRAVIS_PULL_REQUEST" != false ]; then vendor/bin/phpunit; fi
|
||||
- if [ "$TRAVIS_PHP_VERSION" == "5.6" ] && [ "$TRAVIS_PULL_REQUEST" == false ]; then vendor/bin/phpunit --coverage-clover build/logs/clover.xml; fi
|
||||
|
||||
after_script:
|
||||
- if [ "$TRAVIS_PHP_VERSION" == "5.6" ] && [ "$TRAVIS_PULL_REQUEST" == false ]; then wget https://scrutinizer-ci.com/ocular.phar; fi
|
||||
- if [ "$TRAVIS_PHP_VERSION" == "5.6" ] && [ "$TRAVIS_PULL_REQUEST" == false ]; then php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml; fi
|
||||
script: vendor/bin/phpunit
|
||||
|
||||
78
README.md
78
README.md
@@ -1,15 +1,14 @@
|
||||
# Cachet
|
||||
|
||||
[](https://styleci.io/repos/26730195/)
|
||||
[](https://travis-ci.org/CachetHQ/Cachet)
|
||||
[](https://scrutinizer-ci.com/g/CachetHQ/Cachet)
|
||||
[](https://travis-ci.org/CachetHQ/Cachet)
|
||||
[](LICENSE)
|
||||
[](http://translate.cachethq.io/project/cachet)
|
||||
[](http://translate.cachethq.io/project/cachet)
|
||||
[](https://packagist.org/packages/cachethq/cachet)
|
||||
|
||||

|
||||
|
||||
Cachet is a beautiful and powerful open source status page system, a free replacement to services such as StatusPage.io, Status.io and others.
|
||||
Cachet is a beautiful and powerful open source status page system.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -22,27 +21,23 @@ Cachet is a beautiful and powerful open source status page system, a free replac
|
||||
- Metrics
|
||||
- Cross-database support: MySQL, PostgreSQL and SQLite
|
||||
- Subscriber notifications via Email
|
||||
- Two factor authentication, with Google Authenticator
|
||||
|
||||
## Usage in production
|
||||
|
||||
The `master` branch of this repository is a development branch and **should not** be used in production. Instead, please check out the latest tag release.
|
||||
- Two factor authentication using Google Authenticator
|
||||
|
||||
## Requirements
|
||||
|
||||
- PHP 5.5.9+ or newer
|
||||
- PHP 5.5.9 - 7.1
|
||||
- Apache or Nginx server
|
||||
- [Composer](https://getcomposer.org)
|
||||
|
||||
## How to contribute
|
||||
## I'm looking to contribute to this awesome project!
|
||||
|
||||
We're always looking for contributions that improve Cachet. It's easy to get started and you don't even need to know how to write a single line of code!
|
||||
Sweet, we're always looking for contributions that improve Cachet! It's easy to get started and you don't even need to know how to write a single line of code!
|
||||
|
||||
### Contributing as a non-developer/non-designer
|
||||
|
||||
If you're one of the more linguistically talented people in the world who can speak and write more than just English, we're always looking for new [translations](#translations).
|
||||
We're always looking for new [translations](#translations).
|
||||
|
||||
Of course bug reports, feature requests and [documentation](https://docs.cachethq.io) are always being sought after.
|
||||
Of course bug reports, feature requests and [documentation](https://docs.cachethq.io) are always appreciated.
|
||||
|
||||
### Contributing as a designer
|
||||
|
||||
@@ -52,65 +47,60 @@ You'll need to install Node.js, Bower and Gulp.
|
||||
|
||||
To get started you can do the following:
|
||||
|
||||
1. Install Node.js, Bower and Gulp.
|
||||
2. Install the assets; `bower install`
|
||||
3. Modify the SCSS files in `./resources/assets/sass/`
|
||||
4. Run `gulp`
|
||||
1. Install Node.js and our dev dependencies.
|
||||
2. Make your changes to the SCSS files in `./resources/assets/sass/`
|
||||
3. Run `gulp`
|
||||
|
||||
If you're making a lot of changes, you'll find that running `gulp watch` will really help you out!
|
||||
If you're making a lot of changes, you'll find that running `gulp watch` will make life easier for you!
|
||||
|
||||
### Contributing as a developer
|
||||
|
||||
Built using [Laravel](https://laravel.com), Cachet is very easy to jump into. Have a look around you'll find it surprisingly easy!
|
||||
Built using [Laravel](https://laravel.com).
|
||||
|
||||
These extra dependencies are required to develop Cachet:
|
||||
We use these extra dependencies to develop Cachet:
|
||||
|
||||
- Node.js
|
||||
- Bower
|
||||
- Gulp
|
||||
- Git
|
||||
|
||||
Once cloned to your local machine, you'll need some demo data! Simply run `php artisan cachet:seed` to get the demo installation on the go.
|
||||
Once cloned to your local machine, you'll need some demo data! Run `php artisan cachet:seed` to get the demo installation ready for action.
|
||||
|
||||
## Installation, Upgrades and Documentation
|
||||
|
||||
You can now find our documentation at [https://docs.cachethq.io](https://docs.cachethq.io).
|
||||
Documentation is found at [https://docs.cachethq.io](https://docs.cachethq.io).
|
||||
|
||||
Here are some useful quick links:
|
||||
|
||||
- [Installing Cachet](https://docs.cachethq.io/docs/installing-cachet)
|
||||
- [Getting started with Docker](https://docs.cachethq.io/docs/get-started-with-docker)
|
||||
- [Getting started with Vagrant](https://docs.cachethq.io/docs/get-started-with-vagrant)
|
||||
|
||||
### Demo Account
|
||||
|
||||
To test out the demo, you may login to the [Dashboard](https://demo.cachethq.io/dashboard) with the following:
|
||||
|
||||
- **Username:** test or test@test.com
|
||||
- **Password:** test123
|
||||
- **Username:** `test` or `test@test.com`
|
||||
- **Password:** `test123`
|
||||
|
||||
The demo is reset every half hour.
|
||||
|
||||
### Release Notes
|
||||
|
||||
All releases are listed on the [Releases page](https://github.com/CachetHQ/Cachet/releases) of the [Cachet GitHub repository](https://github.com/CachetHQ/Cachet). On the Releases page, you can also find the release notes for each release.
|
||||
We list releases on the [Releases page](https://github.com/CachetHQ/Cachet/releases) of the [Cachet GitHub repository](https://github.com/CachetHQ/Cachet). On the Releases page, you can also find the release notes for each release.
|
||||
|
||||
## Translations
|
||||
|
||||
A special thank you to our [translators](https://crowdin.com/project/cachet/activity_stream), who have allowed us to share Cachet with the world. If you'd like to contribute translations, please check out our [CrowdIn project](https://crowdin.com/project/cachet).
|
||||
If you'd like to contribute translations, please check out our [CrowdIn project](https://crowdin.com/project/cachet).
|
||||
|
||||
> Thank you to our [translators](https://crowdin.com/project/cachet/activity_stream), who have allowed us to share Cachet with the world!
|
||||
|
||||
## Show your support
|
||||
|
||||
Cachet is a BSD-3-licensed open source project.
|
||||
|
||||
## Professional Installation Service
|
||||
|
||||
We offer a professional installation service. To find out more, email us at [support@alt-three.com](mailto:support@alt-three.com?Cachet Installation)
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Cachet, please send an e-mail to us at support@alt-three.com. All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## Installations
|
||||
|
||||
We offer a paid installation service, which starts at $99 but is subject to change, dependant on your setup and infrastructure.
|
||||
|
||||
To find out more, email us at support@alt-three.com
|
||||
|
||||
## Donations
|
||||
|
||||
Cachet is open source, we don't have any licensing models and don't run ads on the [website](https://cachethq.io).
|
||||
|
||||
If you'd like to donate towards further development of Cachet you can do so via Paypal.
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D4M5LVULVPPKL)
|
||||
If you discover a security vulnerability within Cachet, please send an e-mail to us at support@alt-three.com. We handle all security vulnerabilities on a case-by-case basis.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ final class AddMetricPointCommand
|
||||
* @var string[]
|
||||
*/
|
||||
public $rules = [
|
||||
'value' => 'int',
|
||||
'value' => 'numeric',
|
||||
'created_at' => 'string',
|
||||
];
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -70,7 +70,7 @@ class ReportIncidentCommandHandler
|
||||
'visible' => $command->visible,
|
||||
];
|
||||
|
||||
if ($command->template) {
|
||||
if (IncidentTemplate::where('slug', '=', $command->template)->first()) {
|
||||
$data['message'] = $this->parseIncidentTemplate($command->template, $command->template_vars);
|
||||
} else {
|
||||
$data['message'] = $command->message;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -51,7 +51,7 @@ class UpdateMetricPointCommandHandler
|
||||
|
||||
$data = [
|
||||
'metric_id' => $metric->id,
|
||||
'value' => $command->value,
|
||||
'value' => (float) $command->value,
|
||||
];
|
||||
|
||||
if ($createdAt) {
|
||||
|
||||
@@ -14,8 +14,7 @@ namespace CachetHQ\Cachet\Bus\Handlers\Commands\Subscriber;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\VerifySubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasSubscribedEvent;
|
||||
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUpdatedSubscriptionsEvent;
|
||||
use CachetHQ\Cachet\Bus\Exceptions\Subscriber\AlreadySubscribedException;
|
||||
use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
|
||||
@@ -23,6 +22,8 @@ use CachetHQ\Cachet\Models\Subscription;
|
||||
* This is the subscribe subscriber command handler.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
* @author Joe Cohen <joe@alt-three.com>
|
||||
* @author Graham Campbell <graham@alt-three.com>
|
||||
*/
|
||||
class SubscribeSubscriberCommandHandler
|
||||
{
|
||||
@@ -31,37 +32,38 @@ class SubscribeSubscriberCommandHandler
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand $command
|
||||
*
|
||||
* @throws \CachetHQ\Cachet\Exceptions\AlreadySubscribedException
|
||||
*
|
||||
* @return \CachetHQ\Cachet\Models\Subscriber
|
||||
*/
|
||||
public function handle(SubscribeSubscriberCommand $command)
|
||||
{
|
||||
if (Subscriber::where('email', $command->email)->first() && $command->subscriptions === null) {
|
||||
throw new AlreadySubscribedException("Cannot subscribe {$command->email} because they're already subscribed.");
|
||||
if ($subscriber = Subscriber::where('email', $command->email)->first()) {
|
||||
return $subscriber;
|
||||
}
|
||||
|
||||
$subscriber = Subscriber::firstOrCreate(['email' => $command->email]);
|
||||
|
||||
// Decide what to subscribe the subscriber to.
|
||||
if ($subscriptions = $command->subscriptions) {
|
||||
foreach ($subscriptions as $subscription => $subscriptionValue) {
|
||||
Subscription::firstOrCreate([
|
||||
'subscriber_id' => $subscriber->id,
|
||||
$subscription => $subscriptionValue,
|
||||
]);
|
||||
}
|
||||
$components = Component::whereIn('id', $subscriptions)->get();
|
||||
} else {
|
||||
$components = Component::all();
|
||||
}
|
||||
|
||||
if ($subscriber->is_verified === false) {
|
||||
if ($command->verified) {
|
||||
dispatch(new VerifySubscriberCommand($subscriber));
|
||||
} else {
|
||||
event(new SubscriberHasSubscribedEvent($subscriber));
|
||||
}
|
||||
$components->map(function ($component) use ($subscriber) {
|
||||
Subscription::create([
|
||||
'subscriber_id' => $subscriber->id,
|
||||
'component_id' => $component->id,
|
||||
]);
|
||||
});
|
||||
|
||||
if ($command->verified) {
|
||||
dispatch(new VerifySubscriberCommand($subscriber));
|
||||
} else {
|
||||
event(new SubscriberHasUpdatedSubscriptionsEvent($subscriber));
|
||||
event(new SubscriberHasSubscribedEvent($subscriber));
|
||||
}
|
||||
|
||||
$subscriber->load('subscriptions');
|
||||
|
||||
return $subscriber;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace CachetHQ\Cachet\Bus\Handlers\Commands\Subscriber;
|
||||
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUnsubscribedEvent;
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
|
||||
class UnsubscribeSubscriberCommandHandler
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ class SendSubscriberVerificationEmailHandler
|
||||
public function handle(SubscriberHasSubscribedEvent $event)
|
||||
{
|
||||
$mail = [
|
||||
'email' => $event->subscriber->email,
|
||||
'subject' => 'Confirm your subscription.',
|
||||
'link' => route('subscribe.verify', ['code' => $event->subscriber->verify_code]),
|
||||
'email' => $event->subscriber->email,
|
||||
'subject' => 'Confirm your subscription.',
|
||||
'link' => route('subscribe.verify', ['code' => $event->subscriber->verify_code]),
|
||||
];
|
||||
|
||||
$this->mailer->queue([
|
||||
|
||||
@@ -46,9 +46,9 @@ class SendInviteUserEmailHandler
|
||||
public function handle(UserWasInvitedEvent $event)
|
||||
{
|
||||
$mail = [
|
||||
'email' => $event->invite->email,
|
||||
'subject' => 'You have been invited.',
|
||||
'link' => route('signup.invite', ['code' => $event->invite->code]),
|
||||
'email' => $event->invite->email,
|
||||
'subject' => 'You have been invited.',
|
||||
'link' => route('signup.invite', ['code' => $event->invite->code]),
|
||||
];
|
||||
|
||||
$this->mailer->queue([
|
||||
|
||||
38
app/Bus/Middleware/UseDatabaseTransactions.php
Normal file
38
app/Bus/Middleware/UseDatabaseTransactions.php
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class DashboardComposer
|
||||
{
|
||||
$view->withIncidentCount(Incident::notScheduled()->count());
|
||||
$view->withIncidentTemplateCount(IncidentTemplate::count());
|
||||
$view->withComponentCount(Component::all()->count());
|
||||
$view->withComponentCount(Component::count());
|
||||
$view->withSubscriberCount(Subscriber::isVerified()->count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -11,13 +11,38 @@
|
||||
|
||||
namespace CachetHQ\Cachet\Composers;
|
||||
|
||||
use CachetHQ\Cachet\Integrations\Core\System;
|
||||
use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\ComponentGroup;
|
||||
use CachetHQ\Cachet\Models\Incident;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
/**
|
||||
* This is the status page composer.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
class StatusPageComposer
|
||||
{
|
||||
/**
|
||||
* The system instance.
|
||||
*
|
||||
* @var \CachetHQ\Cachet\Integrations\Contracts\System
|
||||
*/
|
||||
protected $system;
|
||||
|
||||
/**
|
||||
* Create a new status page composer instance.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Integrations\Contracts\System $system
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(System $system)
|
||||
{
|
||||
$this->system = $system;
|
||||
}
|
||||
|
||||
/**
|
||||
* Index page view composer.
|
||||
*
|
||||
@@ -27,40 +52,7 @@ class StatusPageComposer
|
||||
*/
|
||||
public function compose(View $view)
|
||||
{
|
||||
$totalComponents = Component::enabled()->count();
|
||||
$majorOutages = Component::enabled()->status(4)->count();
|
||||
$isMajorOutage = $totalComponents ? ($majorOutages / $totalComponents) >= 0.5 : false;
|
||||
|
||||
// Default data
|
||||
$withData = [
|
||||
'system_status' => 'info',
|
||||
'system_message' => trans_choice('cachet.service.bad', $totalComponents),
|
||||
'favicon' => 'favicon-high-alert',
|
||||
];
|
||||
|
||||
if ($isMajorOutage) {
|
||||
$withData = [
|
||||
'system_status' => 'danger',
|
||||
'system_message' => trans_choice('cachet.service.major', $totalComponents),
|
||||
'favicon' => 'favicon-high-alert',
|
||||
];
|
||||
} elseif (Component::enabled()->notStatus(1)->count() === 0) {
|
||||
// If all our components are ok, do we have any non-fixed incidents?
|
||||
$incidents = Incident::notScheduled()->orderBy('created_at', 'desc')->get();
|
||||
$incidentCount = $incidents->count();
|
||||
|
||||
if ($incidentCount === 0 || ($incidentCount >= 1 && (int) $incidents->first()->status === 4)) {
|
||||
$withData = [
|
||||
'system_status' => 'success',
|
||||
'system_message' => trans_choice('cachet.service.good', $totalComponents),
|
||||
'favicon' => 'favicon',
|
||||
];
|
||||
}
|
||||
} else {
|
||||
if (Component::enabled()->whereIn('status', [2, 3])->count() > 0) {
|
||||
$withData['favicon'] = 'favicon-medium-alert';
|
||||
}
|
||||
}
|
||||
$status = $this->system->getStatus();
|
||||
|
||||
// Scheduled maintenance code.
|
||||
$scheduledMaintenance = Incident::scheduled()->orderBy('scheduled_at')->get();
|
||||
@@ -70,7 +62,7 @@ class StatusPageComposer
|
||||
$componentGroups = ComponentGroup::whereIn('id', $usedComponentGroups)->orderBy('order')->get();
|
||||
$ungroupedComponents = Component::enabled()->where('group_id', 0)->orderBy('order')->orderBy('created_at')->get();
|
||||
|
||||
$view->with($withData)
|
||||
$view->with($status)
|
||||
->withComponentGroups($componentGroups)
|
||||
->withUngroupedComponents($ungroupedComponents)
|
||||
->withScheduledMaintenance($scheduledMaintenance);
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace CachetHQ\Cachet\Composers;
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
class TimezoneLocaleComposer
|
||||
@@ -32,7 +33,7 @@ class TimezoneLocaleComposer
|
||||
$langs = array_map(function ($lang) use ($enabledLangs) {
|
||||
$locale = basename($lang);
|
||||
|
||||
return [$locale => $enabledLangs[$locale]];
|
||||
return [$locale => Arr::get($enabledLangs, $locale)];
|
||||
}, glob(base_path('resources/lang').'/*'));
|
||||
|
||||
$langs = call_user_func_array('array_merge', $langs);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
@@ -293,42 +314,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']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
88
app/Foundation/Exceptions/Displayers/ThrottleDisplayer.php
Normal file
88
app/Foundation/Exceptions/Displayers/ThrottleDisplayer.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,20 @@
|
||||
|
||||
namespace CachetHQ\Cachet\Foundation\Providers;
|
||||
|
||||
use CachetHQ\Cachet\Config\Repository;
|
||||
use CachetHQ\Cachet\Models\Setting as SettingModel;
|
||||
use CachetHQ\Cachet\Settings\Cache;
|
||||
use CachetHQ\Cachet\Settings\Repository;
|
||||
use Exception;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Jenssegers\Date\Date;
|
||||
|
||||
/**
|
||||
* This is the config service provider class.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
* @author Graham Campbell <graham@alt-three.com>
|
||||
* @author Joe Cohen <joe@alt-three.com>
|
||||
*/
|
||||
class ConfigServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
@@ -25,8 +34,26 @@ class ConfigServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$env = $this->app->environment();
|
||||
$repo = $this->app->make(Repository::class);
|
||||
$cache = $this->app->make(Cache::class);
|
||||
$loaded = $cache->load($env);
|
||||
|
||||
$this->app->terminating(function () use ($repo, $cache) {
|
||||
if ($repo->stale()) {
|
||||
$cache->clear();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
$this->app->config->set('setting', $this->app->setting->all());
|
||||
if ($loaded === false) {
|
||||
$loaded = $repo->all();
|
||||
$cache->store($env, $loaded);
|
||||
}
|
||||
|
||||
$settings = array_merge($this->app->config->get('setting'), $loaded);
|
||||
|
||||
$this->app->config->set('setting', $settings);
|
||||
} catch (Exception $e) {
|
||||
//
|
||||
}
|
||||
@@ -35,9 +62,10 @@ class ConfigServiceProvider extends ServiceProvider
|
||||
$this->app->config->set('app.url', $appDomain);
|
||||
}
|
||||
|
||||
if ($appLocale = $this->app->config->get('setting.app.locale')) {
|
||||
if ($appLocale = $this->app->config->get('setting.app_locale')) {
|
||||
$this->app->config->set('app.locale', $appLocale);
|
||||
$this->app->translator->setLocale($appLocale);
|
||||
Date::setLocale($appLocale);
|
||||
}
|
||||
|
||||
if ($appTimezone = $this->app->config->get('setting.app_timezone')) {
|
||||
@@ -65,10 +93,12 @@ class ConfigServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton('setting', function () {
|
||||
return new Repository(new SettingModel());
|
||||
$this->app->singleton(Cache::class, function ($app) {
|
||||
return new Cache($app->files, $app->bootstrapPath().'/cachet');
|
||||
});
|
||||
|
||||
$this->app->alias('setting', Repository::class);
|
||||
$this->app->singleton(Repository::class, function () {
|
||||
return new Repository(new SettingModel());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
],
|
||||
|
||||
47
app/Foundation/Providers/IntegrationServiceProvider.php
Normal file
47
app/Foundation/Providers/IntegrationServiceProvider.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Foundation\Providers;
|
||||
|
||||
use CachetHQ\Cachet\Integrations\Contracts\System as SystemContract;
|
||||
use CachetHQ\Cachet\Integrations\Core\System;
|
||||
use Illuminate\Contracts\Container\Container;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
/**
|
||||
* This is the integration service provider.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
class IntegrationServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->registerSystem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the system class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerSystem()
|
||||
{
|
||||
$this->app->singleton(SystemContract::class, function (Container $app) {
|
||||
return new System();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ class ComponentController extends AbstractApiController
|
||||
$tags = preg_split('/ ?, ?/', Binput::get('tags'));
|
||||
|
||||
// For every tag, do we need to create it?
|
||||
$componentTags = array_map(function ($taggable) use ($component) {
|
||||
$componentTags = array_map(function ($taggable) {
|
||||
return Tag::firstOrCreate([
|
||||
'name' => $taggable,
|
||||
])->id;
|
||||
@@ -128,7 +128,7 @@ class ComponentController extends AbstractApiController
|
||||
$tags = preg_split('/ ?, ?/', Binput::get('tags'));
|
||||
|
||||
// For every tag, do we need to create it?
|
||||
$componentTags = array_map(function ($taggable) use ($component) {
|
||||
$componentTags = array_map(function ($taggable) {
|
||||
return Tag::firstOrCreate(['name' => $taggable])->id;
|
||||
}, $tags);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
|
||||
namespace CachetHQ\Cachet\Http\Controllers\Api;
|
||||
|
||||
use CachetHQ\Cachet\Integrations\Contracts\System;
|
||||
use CachetHQ\Cachet\Integrations\Releases;
|
||||
|
||||
/**
|
||||
* This is the general api controller.
|
||||
*
|
||||
* @author James Brooks <james@bluebaytravel.co.uk>
|
||||
*/
|
||||
class GeneralController extends AbstractApiController
|
||||
{
|
||||
/**
|
||||
@@ -30,6 +38,26 @@ class GeneralController extends AbstractApiController
|
||||
*/
|
||||
public function version()
|
||||
{
|
||||
return $this->item(CACHET_VERSION);
|
||||
$latest = app(Releases::class)->latest();
|
||||
|
||||
return $this->setMetaData([
|
||||
'on_latest' => version_compare(CACHET_VERSION, $latest['tag_name']) === 1,
|
||||
'latest' => $latest,
|
||||
])->item(CACHET_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the system status message.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function status()
|
||||
{
|
||||
$system = app()->make(System::class)->getStatus();
|
||||
|
||||
return $this->item([
|
||||
'status' => $system['system_status'],
|
||||
'message' => $system['system_message'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -11,34 +11,46 @@
|
||||
|
||||
namespace CachetHQ\Cachet\Http\Controllers\Dashboard;
|
||||
|
||||
use CachetHQ\Cachet\GitHub\Release;
|
||||
use CachetHQ\Cachet\Bus\Commands\Component\UpdateComponentCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\ComponentGroup\UpdateComponentGroupCommand;
|
||||
use CachetHQ\Cachet\Http\Controllers\Api\AbstractApiController;
|
||||
use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\ComponentGroup;
|
||||
use CachetHQ\Cachet\Models\IncidentTemplate;
|
||||
use Exception;
|
||||
use GrahamCampbell\Binput\Facades\Binput;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
class ApiController extends Controller
|
||||
class ApiController extends AbstractApiController
|
||||
{
|
||||
/**
|
||||
* Updates a component with the entered info.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Models\Component $component
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
|
||||
*
|
||||
* @return \CachetHQ\Cachet\Models\Component
|
||||
*/
|
||||
public function postUpdateComponent(Component $component)
|
||||
{
|
||||
if (!$component->update(Binput::except(['_token']))) {
|
||||
throw new Exception(trans('dashboard.components.edit.failure'));
|
||||
try {
|
||||
dispatch(new UpdateComponentCommand(
|
||||
$component,
|
||||
$component->name,
|
||||
$component->description,
|
||||
Binput::get('status'),
|
||||
$component->link,
|
||||
$component->order,
|
||||
$component->group_id,
|
||||
$component->enabled
|
||||
));
|
||||
} catch (QueryException $e) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
return $component;
|
||||
return $this->item($component);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,11 +63,25 @@ class ApiController extends Controller
|
||||
$componentData = Binput::get('ids');
|
||||
|
||||
foreach ($componentData as $order => $componentId) {
|
||||
// Ordering should be 1-based, data comes in 0-based
|
||||
Component::find($componentId)->update(['order' => $order + 1]);
|
||||
try {
|
||||
$component = Component::find($componentId);
|
||||
|
||||
dispatch(new UpdateComponentCommand(
|
||||
$component,
|
||||
$component->name,
|
||||
$component->description,
|
||||
$component->status,
|
||||
$component->link,
|
||||
$order + 1,
|
||||
$component->group_id,
|
||||
$component->enabled
|
||||
));
|
||||
} catch (QueryException $e) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
}
|
||||
|
||||
return $componentData;
|
||||
return $this->collection(Component::query()->orderBy('order')->get());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,10 +94,17 @@ class ApiController extends Controller
|
||||
$groupData = Binput::get('ids');
|
||||
|
||||
foreach ($groupData as $order => $groupId) {
|
||||
ComponentGroup::find($groupId)->update(['order' => $order + 1]);
|
||||
$group = ComponentGroup::find($groupId);
|
||||
|
||||
dispatch(new UpdateComponentGroupCommand(
|
||||
$group,
|
||||
$group->name,
|
||||
$order + 1,
|
||||
$group->collapsed
|
||||
));
|
||||
}
|
||||
|
||||
return $groupData;
|
||||
return $this->collection(ComponentGroup::query()->orderBy('order')->get());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,20 +124,4 @@ class ApiController extends Controller
|
||||
|
||||
throw new ModelNotFoundException("Incident template for $templateSlug could not be found.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if Cachet is up to date.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function checkVersion()
|
||||
{
|
||||
$latest = app(Release::class)->latest();
|
||||
|
||||
return Response::json([
|
||||
'cachet_version' => CACHET_VERSION,
|
||||
'latest_version' => $latest,
|
||||
'is_latest' => version_compare(CACHET_VERSION, $latest) === 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ class ComponentController extends Controller
|
||||
$tags = preg_split('/ ?, ?/', $tags);
|
||||
|
||||
// For every tag, do we need to create it?
|
||||
$componentTags = array_map(function ($taggable) use ($component) {
|
||||
$componentTags = array_map(function ($taggable) {
|
||||
return Tag::firstOrCreate(['name' => $taggable])->id;
|
||||
}, $tags);
|
||||
|
||||
@@ -201,7 +201,7 @@ class ComponentController extends Controller
|
||||
$tags = preg_split('/ ?, ?/', $tags);
|
||||
|
||||
// For every tag, do we need to create it?
|
||||
$componentTags = array_map(function ($taggable) use ($component) {
|
||||
$componentTags = array_map(function ($taggable) {
|
||||
return Tag::firstOrCreate(['name' => $taggable])->id;
|
||||
}, $tags);
|
||||
|
||||
|
||||
@@ -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,25 @@ 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()) {
|
||||
if (is_object($feed)) {
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
namespace CachetHQ\Cachet\Http\Controllers\Dashboard;
|
||||
|
||||
use CachetHQ\Cachet\Integrations\Credits;
|
||||
use CachetHQ\Cachet\Models\User;
|
||||
use CachetHQ\Cachet\Settings\Repository;
|
||||
use Exception;
|
||||
use GrahamCampbell\Binput\Facades\Binput;
|
||||
use Illuminate\Routing\Controller;
|
||||
@@ -19,8 +21,8 @@ use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Str;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
@@ -81,6 +83,12 @@ class SettingsController extends Controller
|
||||
'icon' => 'ion-stats-bars',
|
||||
'active' => false,
|
||||
],
|
||||
'credits' => [
|
||||
'title' => trans('dashboard.settings.credits.credits'),
|
||||
'url' => route('dashboard.settings.credits'),
|
||||
'icon' => 'ion-ios-list',
|
||||
'active' => false,
|
||||
],
|
||||
'about' => [
|
||||
'title' => CACHET_VERSION,
|
||||
'url' => 'javascript: void(0);',
|
||||
@@ -211,6 +219,30 @@ class SettingsController extends Controller
|
||||
->withSubMenu($this->subMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the credits view.
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showCreditsView()
|
||||
{
|
||||
$this->subMenu['credits']['active'] = true;
|
||||
|
||||
$credits = app(Credits::class)->latest();
|
||||
|
||||
$backers = $credits['backers'];
|
||||
$contributors = $credits['contributors'];
|
||||
|
||||
shuffle($backers);
|
||||
shuffle($contributors);
|
||||
|
||||
return View::make('dashboard.settings.credits')
|
||||
->withPageTitle(trans('dashboard.settings.credits.credits').' - '.trans('dashboard.dashboard'))
|
||||
->withBackers($backers)
|
||||
->withContributors($contributors)
|
||||
->withSubMenu($this->subMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the status page settings.
|
||||
*
|
||||
@@ -218,9 +250,7 @@ class SettingsController extends Controller
|
||||
*/
|
||||
public function postSettings()
|
||||
{
|
||||
$redirectUrl = Session::get('redirect_to', route('dashboard.settings.setup'));
|
||||
|
||||
$setting = app('setting');
|
||||
$setting = app(Repository::class);
|
||||
|
||||
if (Binput::get('remove_banner') === '1') {
|
||||
$setting->set('app_banner', null);
|
||||
@@ -244,30 +274,16 @@ class SettingsController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($parameters['stylesheet'])) {
|
||||
if ($stylesheet = Binput::get('stylesheet', null, false, false)) {
|
||||
$setting->set('stylesheet', $stylesheet);
|
||||
} else {
|
||||
$setting->delete('stylesheet');
|
||||
}
|
||||
}
|
||||
|
||||
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 = [
|
||||
@@ -276,6 +292,7 @@ class SettingsController extends Controller
|
||||
'remove_banner',
|
||||
'header',
|
||||
'footer',
|
||||
'stylesheet',
|
||||
];
|
||||
|
||||
try {
|
||||
@@ -287,13 +304,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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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'], '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,16 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,8 +15,9 @@ use AltThree\Validator\ValidationException;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriptionCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UpdateSubscriberSubscriptionCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\VerifySubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Exceptions\Subscriber\AlreadySubscribedException;
|
||||
use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
use GrahamCampbell\Binput\Facades\Binput;
|
||||
@@ -57,20 +58,22 @@ class SubscribeController extends Controller
|
||||
$subscriptions = Binput::get('subscriptions');
|
||||
|
||||
try {
|
||||
dispatch(new SubscribeSubscriberCommand($email, false, $subscriptions));
|
||||
} catch (AlreadySubscribedException $e) {
|
||||
return Redirect::route('subscribe.subscribe')
|
||||
->withTitle(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
|
||||
->withErrors(trans('cachet.subscriber.email.already-subscribed', ['email' => $email]));
|
||||
$verified = false;
|
||||
|
||||
$subscription = dispatch(new SubscribeSubscriberCommand($email, $verified));
|
||||
} catch (ValidationException $e) {
|
||||
return Redirect::route('subscribe.subscribe')
|
||||
return Redirect::route('status-page')
|
||||
->withInput(Binput::all())
|
||||
->withTitle(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
|
||||
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
|
||||
->withErrors($e->getMessageBag());
|
||||
}
|
||||
|
||||
return Redirect::route('status-page')
|
||||
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.subscribed')));
|
||||
$message = $subscription->is_verified ?
|
||||
trans('cachet.subscriber.email.already-subscribed', ['email' => $email]) :
|
||||
trans('cachet.subscriber.email.subscribed');
|
||||
|
||||
return Redirect::route('subscribe.manage', $subscription->verify_code)
|
||||
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), $message));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,16 +89,18 @@ class SubscribeController extends Controller
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$subscriber = Subscriber::where('verify_code', '=', $code)->first();
|
||||
$subscriber = Subscriber::where('verify_code', $code)->first();
|
||||
|
||||
if (!$subscriber || $subscriber->is_verified) {
|
||||
if (!$subscriber) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
dispatch(new VerifySubscriberCommand($subscriber));
|
||||
if (!$subscriber->is_verified) {
|
||||
dispatch(new VerifySubscriberCommand($subscriber));
|
||||
}
|
||||
|
||||
return Redirect::route('status-page')
|
||||
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.verified')));
|
||||
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.verified')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,7 +130,7 @@ class SubscribeController extends Controller
|
||||
}
|
||||
|
||||
return Redirect::route('status-page')
|
||||
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.unsubscribed')));
|
||||
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.unsubscribed')));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,10 +148,45 @@ class SubscribeController extends Controller
|
||||
|
||||
$subscriber = Subscriber::where('verify_code', '=', $code)->first();
|
||||
|
||||
if (!$subscriber || !$subscriber->is_verified) {
|
||||
if (!$subscriber) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
return View::make('subscribe.manage')->withSubscriber($subscriber);
|
||||
return View::make('subscribe.manage')
|
||||
->withComponents(Component::all())
|
||||
->withSubscriber($subscriber)
|
||||
->withSubscriptions($subscriber->subscriptions->pluck('component_id')->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the subscription manager for a subscriber.
|
||||
*
|
||||
* @param string|null $code
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function postManage($code = null)
|
||||
{
|
||||
if ($code === null) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$subscriber = Subscriber::where('verify_code', '=', $code)->first();
|
||||
|
||||
if (!$subscriber) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
try {
|
||||
dispatch(new UpdateSubscriberSubscriptionCommand($subscriber, Binput::get('subscriptions')));
|
||||
} catch (ValidationException $e) {
|
||||
return Redirect::route('subscribe.manage', $subscriber->verify_code)
|
||||
->withInput(Binput::all())
|
||||
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
|
||||
->withErrors($e->getMessageBag());
|
||||
}
|
||||
|
||||
return Redirect::route('subscribe.manage', $subscriber->verify_code)
|
||||
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.subscribed')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -33,6 +33,7 @@ class ApiRoutes
|
||||
$router->group(['middleware' => ['auth.api']], function (Registrar $router) {
|
||||
$router->get('ping', 'GeneralController@ping');
|
||||
$router->get('version', 'GeneralController@version');
|
||||
$router->get('status', 'GeneralController@status');
|
||||
|
||||
$router->get('components', 'ComponentController@getComponents');
|
||||
$router->get('components/groups', 'ComponentGroupController@getGroups');
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
27
app/Integrations/Contracts/System.php
Normal file
27
app/Integrations/Contracts/System.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Integrations\Contracts;
|
||||
|
||||
/**
|
||||
* This is the system interface.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
interface System
|
||||
{
|
||||
/**
|
||||
* Get the entire system status.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStatus();
|
||||
}
|
||||
70
app/Integrations/Core/System.php
Normal file
70
app/Integrations/Core/System.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Integrations\Core;
|
||||
|
||||
use CachetHQ\Cachet\Integrations\Contracts\System as SystemContract;
|
||||
use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\Incident;
|
||||
|
||||
/**
|
||||
* This is the core system class.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
class System implements SystemContract
|
||||
{
|
||||
/**
|
||||
* Get the entire system status.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStatus()
|
||||
{
|
||||
$enabledScope = Component::enabled();
|
||||
$totalComponents = Component::enabled()->count();
|
||||
$majorOutages = Component::enabled()->status(4)->count();
|
||||
$isMajorOutage = $totalComponents ? ($majorOutages / $totalComponents) >= 0.5 : false;
|
||||
|
||||
// Default data
|
||||
$status = [
|
||||
'system_status' => 'info',
|
||||
'system_message' => trans_choice('cachet.service.bad', $totalComponents),
|
||||
'favicon' => 'favicon-high-alert',
|
||||
];
|
||||
|
||||
if ($isMajorOutage) {
|
||||
$status = [
|
||||
'system_status' => 'danger',
|
||||
'system_message' => trans_choice('cachet.service.major', $totalComponents),
|
||||
'favicon' => 'favicon-high-alert',
|
||||
];
|
||||
} elseif (Component::enabled()->notStatus(1)->count() === 0) {
|
||||
// If all our components are ok, do we have any non-fixed incidents?
|
||||
$incidents = Incident::notScheduled()->orderBy('created_at', 'desc')->get()->filter(function ($incident) {
|
||||
return $incident->status > 0;
|
||||
});
|
||||
$incidentCount = $incidents->count();
|
||||
|
||||
if ($incidentCount === 0 || ($incidentCount >= 1 && (int) $incidents->first()->status === 4)) {
|
||||
$status = [
|
||||
'system_status' => 'success',
|
||||
'system_message' => trans_choice('cachet.service.good', $totalComponents),
|
||||
'favicon' => 'favicon',
|
||||
];
|
||||
}
|
||||
} elseif (Component::enabled()->whereIn('status', [2, 3])->count() > 0) {
|
||||
$status['favicon'] = 'favicon-medium-alert';
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
81
app/Integrations/Credits.php
Normal file
81
app/Integrations/Credits.php
Normal 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
88
app/Integrations/Feed.php
Normal 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;
|
||||
}
|
||||
}
|
||||
95
app/Integrations/Releases.php
Normal file
95
app/Integrations/Releases.php
Normal 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'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,10 @@ use McCool\LaravelAutoPresenter\HasPresenter;
|
||||
|
||||
class Component extends Model implements HasPresenter
|
||||
{
|
||||
use SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
|
||||
use SearchableTrait;
|
||||
use SoftDeletes;
|
||||
use SortableTrait;
|
||||
use ValidatingTrait;
|
||||
|
||||
/**
|
||||
* List of attributes that have default values.
|
||||
|
||||
@@ -20,7 +20,9 @@ use McCool\LaravelAutoPresenter\HasPresenter;
|
||||
|
||||
class ComponentGroup extends Model implements HasPresenter
|
||||
{
|
||||
use SearchableTrait, SortableTrait, ValidatingTrait;
|
||||
use SearchableTrait;
|
||||
use SortableTrait;
|
||||
use ValidatingTrait;
|
||||
|
||||
/**
|
||||
* The model's attributes.
|
||||
@@ -119,7 +121,7 @@ class ComponentGroup extends Model implements HasPresenter
|
||||
*/
|
||||
public function enabled_components()
|
||||
{
|
||||
return $this->components()->enabled();
|
||||
return $this->components()->enabled()->orderBy('order');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,13 +16,17 @@ 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;
|
||||
|
||||
class Incident extends Model implements HasPresenter
|
||||
{
|
||||
use SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
|
||||
use SearchableTrait;
|
||||
use SoftDeletes;
|
||||
use SortableTrait;
|
||||
use ValidatingTrait;
|
||||
|
||||
/**
|
||||
* The attributes that should be casted to native types.
|
||||
@@ -71,6 +75,7 @@ class Incident extends Model implements HasPresenter
|
||||
*/
|
||||
protected $searchable = [
|
||||
'id',
|
||||
'component_id',
|
||||
'name',
|
||||
'status',
|
||||
'visible',
|
||||
@@ -96,7 +101,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 +113,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 +125,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());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,14 @@ 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;
|
||||
|
||||
class Metric extends Model implements HasPresenter
|
||||
{
|
||||
use SortableTrait, ValidatingTrait;
|
||||
use SortableTrait;
|
||||
use ValidatingTrait;
|
||||
|
||||
/**
|
||||
* The calculation type of sum.
|
||||
@@ -47,6 +49,8 @@ class Metric extends Model implements HasPresenter
|
||||
'calc_type' => 0,
|
||||
'places' => 2,
|
||||
'default_view' => 1,
|
||||
'threshold' => 5,
|
||||
'order' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -61,6 +65,8 @@ class Metric extends Model implements HasPresenter
|
||||
'calc_type' => 'int',
|
||||
'places' => 'int',
|
||||
'default_view' => 'int',
|
||||
'threshold' => 'int',
|
||||
'order' => 'int',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -77,6 +83,8 @@ class Metric extends Model implements HasPresenter
|
||||
'calc_type',
|
||||
'places',
|
||||
'default_view',
|
||||
'threshold',
|
||||
'order',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -91,6 +99,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 +114,7 @@ class Metric extends Model implements HasPresenter
|
||||
'display_chart',
|
||||
'default_value',
|
||||
'calc_type',
|
||||
'order',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -116,6 +127,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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,11 @@ trait SearchableTrait
|
||||
return $query;
|
||||
}
|
||||
|
||||
if (!array_intersect(array_keys($search), $this->searchable)) {
|
||||
$allowed_search = array_intersect_key($search, array_flip($this->searchable));
|
||||
if (!$allowed_search) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
return $query->where($search);
|
||||
return $query->where($allowed_search);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
|
||||
{
|
||||
use Authenticatable, CanResetPassword, ValidatingTrait;
|
||||
use Authenticatable;
|
||||
use CanResetPassword;
|
||||
use ValidatingTrait;
|
||||
|
||||
/**
|
||||
* The admin level of user.
|
||||
@@ -116,7 +118,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
*/
|
||||
public function getGravatarAttribute($size = 200)
|
||||
{
|
||||
return sprintf('https://www.gravatar.com/avatar/%s?size=%d', md5($this->email), $size);
|
||||
return sprintf('https://www.gravatar.com/avatar/%s?size=%d', md5(strtolower($this->email)), $size);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
87
app/Repositories/Metric/AbstractMetricRepository.php
Normal file
87
app/Repositories/Metric/AbstractMetricRepository.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?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 table names prefix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getPrefix()
|
||||
{
|
||||
$driver = $this->config->get('database.default');
|
||||
$connection = $this->config->get('database.connections.'.$driver);
|
||||
$prefix = $connection['prefix'];
|
||||
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metrics table name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTableName()
|
||||
{
|
||||
$prefix = $this->getPrefix();
|
||||
|
||||
return $prefix.'metrics';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metric points table name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getMetricPointsTableName()
|
||||
{
|
||||
$prefix = $this->getPrefix();
|
||||
|
||||
return $prefix.'metric_points';
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
@@ -31,15 +36,23 @@ 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)'));
|
||||
$metricPointsTableName = $this->getMetricPointsTableName();
|
||||
|
||||
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 $metricPointsTableName 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) {
|
||||
@@ -61,15 +74,23 @@ 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)'));
|
||||
$metricPointsTableName = $this->getMetricPointsTableName();
|
||||
|
||||
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 $metricPointsTableName 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) {
|
||||
@@ -89,16 +110,23 @@ class MySqlRepository implements MetricInterface
|
||||
public function getPointsForDayInWeek(Metric $metric, $day)
|
||||
{
|
||||
$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")'));
|
||||
$metricPointsTableName = $this->getMetricPointsTableName();
|
||||
|
||||
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 $metricPointsTableName 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) {
|
||||
|
||||
@@ -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.
|
||||
@@ -29,27 +34,26 @@ class PgSqlRepository implements MetricInterface
|
||||
*/
|
||||
public function getPointsLastHour(Metric $metric, $hour, $minute)
|
||||
{
|
||||
$metricPointsTableName = $this->getMetricPointsTableName();
|
||||
$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($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$queryType = 'avg(metric_points.value)';
|
||||
$queryType = "avg($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} else {
|
||||
$queryType = 'sum(metric_points.value)';
|
||||
$queryType = "sum($metricPointsTableName.value * $metricPointsTableName.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 $metricPointsTableName ON $metricPointsTableName.metric_id = m.id WHERE m.id = :metricId AND to_char($metricPointsTableName.created_at, 'YYYYMMDDHH24MI') = :timeInterval GROUP BY to_char($metricPointsTableName.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 +74,25 @@ class PgSqlRepository implements MetricInterface
|
||||
public function getPointsByHour(Metric $metric, $hour)
|
||||
{
|
||||
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
|
||||
$hourInterval = $dateTime->format('YmdH');
|
||||
$metricPointsTableName = $this->getMetricPointsTableName();
|
||||
|
||||
// Default metrics calculations.
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$queryType = 'sum(metric_points.value)';
|
||||
$queryType = "sum($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$queryType = 'avg(metric_points.value)';
|
||||
$queryType = "avg($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} else {
|
||||
$queryType = 'sum(metric_points.value)';
|
||||
$queryType = "sum($metricPointsTableName.value * $metricPointsTableName.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 $metricPointsTableName ON $metricPointsTableName.metric_id = m.id WHERE $metricPointsTableName.metric_id = :metricId AND to_char($metricPointsTableName.created_at, 'YYYYMMDDHH24') = :timeInterval GROUP BY to_char($metricPointsTableName.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) {
|
||||
@@ -109,16 +112,22 @@ class PgSqlRepository implements MetricInterface
|
||||
public function getPointsForDayInWeek(Metric $metric, $day)
|
||||
{
|
||||
$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\')'));
|
||||
$metricPointsTableName = $this->getMetricPointsTableName();
|
||||
|
||||
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 $metricPointsTableName 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) {
|
||||
|
||||
@@ -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,25 @@ 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)'));
|
||||
$metricPointsTableName = $this->getMetricPointsTableName();
|
||||
|
||||
// Default metrics calculations.
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$value = $points->sum('value');
|
||||
$queryType = "sum($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = "avg($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} else {
|
||||
$queryType = "sum($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
$query = DB::select("select {$queryType} as value FROM {$this->getTableName()} m JOIN $metricPointsTableName ON $metricPointsTableName.metric_id = m.id WHERE m.id = :metricId AND strftime('%Y%m%d%H%M', $metricPointsTableName.created_at) = :timeInterval GROUP BY strftime('%H%M', $metricPointsTableName.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 +69,25 @@ 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)'));
|
||||
$metricPointsTableName = $this->getMetricPointsTableName();
|
||||
|
||||
// Default metrics calculations.
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$value = $points->sum('value');
|
||||
$queryType = "sum($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = "avg($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} else {
|
||||
$queryType = "sum($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
$query = DB::select("select {$queryType} as value FROM {$this->getTableName()} m JOIN $metricPointsTableName ON $metricPointsTableName.metric_id = m.id WHERE m.id = :metricId AND strftime('%Y%m%d%H', $metricPointsTableName.created_at) = :timeInterval GROUP BY strftime('%H', $metricPointsTableName.created_at)", [
|
||||
'metricId' => $metric->id,
|
||||
'timeInterval' => $dateTime->format('YmdH'),
|
||||
]);
|
||||
|
||||
if (isset($query[0])) {
|
||||
$value = $query[0]->value;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
@@ -89,16 +107,25 @@ class SqliteRepository implements MetricInterface
|
||||
public function getPointsForDayInWeek(Metric $metric, $day)
|
||||
{
|
||||
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
|
||||
$metricPointsTableName = $this->getMetricPointsTableName();
|
||||
|
||||
$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($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = "avg($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
} else {
|
||||
$queryType = "sum($metricPointsTableName.value * $metricPointsTableName.counter)";
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
$query = DB::select("select {$queryType} as value FROM {$this->getTableName()} m JOIN $metricPointsTableName ON $metricPointsTableName.metric_id = m.id WHERE m.id = :metricId AND $metricPointsTableName.created_at > date('now', '-7 day') AND strftime('%Y%m%d', $metricPointsTableName.created_at) = :timeInterval GROUP BY strftime('%Y%m%d', $metricPointsTableName.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
103
app/Settings/Cache.php
Normal 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";
|
||||
}
|
||||
}
|
||||
@@ -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?
|
||||
*
|
||||
110
app/Subscribers/CommandSubscriber.php
Normal file
110
app/Subscribers/CommandSubscriber.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Subscribers;
|
||||
|
||||
use CachetHQ\Cachet\Settings\Cache;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Config\Repository;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
/**
|
||||
* This is the command subscriber class.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
* @author Graham Campbell <graham@alt-three.com>
|
||||
*/
|
||||
class CommandSubscriber
|
||||
{
|
||||
/**
|
||||
* The settings cache instance.
|
||||
*
|
||||
* @var \CachetHQ\Cachet\Settings\Cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The config repository instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Create a new command subscriber instance.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Settings\Cache $cache
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Cache $cache, Repository $config)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $events
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen('command.installing', __CLASS__.'@fire', 5);
|
||||
$events->listen('command.updating', __CLASS__.'@fire', 5);
|
||||
$events->listen('command.resetting', __CLASS__.'@fire', 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the settings cache, and backup the databases.
|
||||
*
|
||||
* @param \Illuminate\Console\Command $command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Command $command)
|
||||
{
|
||||
$command->line('Clearing settings cache...');
|
||||
|
||||
$this->cache->clear();
|
||||
|
||||
$command->line('Settings cache cleared!');
|
||||
|
||||
// SQLite does not backup.
|
||||
if ($this->config->get('database.default') === 'sqlite') {
|
||||
$command->line('Backup skipped: SQLite is not supported.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$command->line('Backing up database...');
|
||||
|
||||
try {
|
||||
$command->call('db:backup', [
|
||||
'--compression' => 'gzip',
|
||||
'--database' => $this->config->get('database.default'),
|
||||
'--destination' => 'local',
|
||||
'--destinationPath' => Carbon::now()->format('Y-m-d H.i.s'),
|
||||
'--no-interaction' => true,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$command->error($e->getMessage());
|
||||
$command->line('Backup skipped!');
|
||||
}
|
||||
|
||||
$command->line('Backup completed!');
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ use Illuminate\Support\Facades\Request;
|
||||
use Jenssegers\Date\Date;
|
||||
|
||||
if (!function_exists('set_active')) {
|
||||
|
||||
/**
|
||||
* Set active class if request is in path.
|
||||
*
|
||||
0
bootstrap/cache/.gitignore
vendored
Executable file → Normal file
0
bootstrap/cache/.gitignore
vendored
Executable file → Normal file
2
bootstrap/cachet/.gitignore
vendored
Normal file
2
bootstrap/cachet/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
23
bower.json
23
bower.json
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -20,31 +20,38 @@
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"laravel/framework": "~5.2.31",
|
||||
"alt-three/badger": "^3.0",
|
||||
"alt-three/bus": "^1.0",
|
||||
"alt-three/emoji": "^3.0",
|
||||
"alt-three/validator": "^1.4",
|
||||
"ext-xml": "*",
|
||||
"alt-three/badger": "^3.1",
|
||||
"alt-three/bus": "^1.1",
|
||||
"alt-three/emoji": "^3.1",
|
||||
"alt-three/throttle": "^1.0",
|
||||
"alt-three/validator": "^1.5",
|
||||
"aws/aws-sdk-php": "^3.7",
|
||||
"backup-manager/laravel": "dev-master#df53f9c9d8c6be5d7a2638f45d54b8fb7bc51e2b",
|
||||
"barryvdh/laravel-cors": "^0.8",
|
||||
"doctrine/dbal": "^2.5",
|
||||
"fedeisas/laravel-mail-css-inliner": "^1.5",
|
||||
"fideloper/proxy": "^3.1",
|
||||
"graham-campbell/binput": "^3.3",
|
||||
"graham-campbell/core": "^4.2",
|
||||
"graham-campbell/markdown": "^6.0",
|
||||
"graham-campbell/exceptions": "^8.3",
|
||||
"guzzlehttp/guzzle": "^6.2",
|
||||
"jenssegers/date": "^3.1",
|
||||
"mccool/laravel-auto-presenter": "^4.2",
|
||||
"graham-campbell/binput": "^3.4",
|
||||
"graham-campbell/core": "^5.1",
|
||||
"graham-campbell/exceptions": "^9.4",
|
||||
"graham-campbell/markdown": "^6.1",
|
||||
"guzzlehttp/guzzle": "6.3.3",
|
||||
"guzzlehttp/psr7": "1.4.2",
|
||||
"jenssegers/date": "^3.2",
|
||||
"laravel/framework": "5.2.39",
|
||||
"mccool/laravel-auto-presenter": "^4.3",
|
||||
"pragmarx/google2fa": "^0.7.1",
|
||||
"predis/predis": "^1.1",
|
||||
"rcrowe/twigbridge": "^0.9.2",
|
||||
"roumen/feed": "^2.10"
|
||||
"roumen/feed": "^2.10.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"alt-three/testbench": "^1.1",
|
||||
"filp/whoops": "^2.0",
|
||||
"fzaninotto/faker": "^1.5",
|
||||
"alt-three/testbench": "^1.7",
|
||||
"filp/whoops": "^2.1",
|
||||
"fzaninotto/faker": "^1.6",
|
||||
"graham-campbell/testbench-core": "^1.1",
|
||||
"mockery/mockery": "0.9.4",
|
||||
"mockery/mockery": "0.9.9",
|
||||
"phpunit/phpunit": "4.8.21",
|
||||
"symfony/css-selector": "^3.0",
|
||||
"symfony/dom-crawler": "^3.0"
|
||||
@@ -54,7 +61,7 @@
|
||||
"database"
|
||||
],
|
||||
"files": [
|
||||
"app/Http/helpers.php"
|
||||
"app/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"CachetHQ\\Cachet\\": "app/"
|
||||
@@ -94,7 +101,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.2-dev"
|
||||
"dev-master": "2.3-dev"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
|
||||
1944
composer.lock
generated
1944
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -162,7 +162,9 @@ return [
|
||||
*/
|
||||
'AltThree\Badger\BadgerServiceProvider',
|
||||
'AltThree\Emoji\EmojiServiceProvider',
|
||||
'BackupManager\Laravel\Laravel5ServiceProvider',
|
||||
'Barryvdh\Cors\ServiceProvider',
|
||||
'Fedeisas\LaravelMailCssInliner\LaravelMailCssInlinerServiceProvider',
|
||||
'Fideloper\Proxy\TrustedProxyServiceProvider',
|
||||
'GrahamCampbell\Binput\BinputServiceProvider',
|
||||
'GrahamCampbell\Exceptions\ExceptionsServiceProvider',
|
||||
@@ -180,9 +182,10 @@ return [
|
||||
*/
|
||||
'CachetHQ\Cachet\Foundation\Providers\AppServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\ComposerServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\ConsoleServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\ConfigServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\IntegrationServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\EventServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\GitHubServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\RepositoryServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\RouteServiceProvider',
|
||||
|
||||
@@ -231,8 +234,8 @@ return [
|
||||
'Validator' => 'Illuminate\Support\Facades\Validator',
|
||||
'View' => 'Illuminate\Support\Facades\View',
|
||||
|
||||
'Binput' => 'GrahamCampbell\Binput\Facades\Binput',
|
||||
'Str' => 'Illuminate\Support\Str',
|
||||
'Binput' => 'GrahamCampbell\Binput\Facades\Binput',
|
||||
'Str' => 'Illuminate\Support\Str',
|
||||
|
||||
],
|
||||
|
||||
|
||||
70
config/backup-manager.php
Normal file
70
config/backup-manager.php
Normal 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
41
config/css-inliner.php
Normal 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,
|
||||
|
||||
];
|
||||
@@ -76,25 +76,25 @@ return [
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'host' => env('DB_HOST', null),
|
||||
'database' => env('DB_DATABASE', null),
|
||||
'username' => env('DB_USERNAME', null),
|
||||
'password' => env('DB_PASSWORD', null),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => env('DB_PREFIX', null),
|
||||
'schema' => env('DB_SCHEMA', 'public'),
|
||||
'driver' => 'pgsql',
|
||||
'host' => env('DB_HOST', null),
|
||||
'database' => env('DB_DATABASE', null),
|
||||
'username' => env('DB_USERNAME', null),
|
||||
'password' => env('DB_PASSWORD', null),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => env('DB_PREFIX', null),
|
||||
'schema' => env('DB_SCHEMA', 'public'),
|
||||
],
|
||||
|
||||
'sqlsrv' => [
|
||||
'driver' => 'sqlsrv',
|
||||
'host' => env('DB_HOST', null),
|
||||
'database' => env('DB_DATABASE', null),
|
||||
'username' => env('DB_USERNAME', null),
|
||||
'password' => env('DB_PASSWORD', null),
|
||||
'port' => env('DB_PORT', null),
|
||||
'prefix' => env('DB_PREFIX', null),
|
||||
'driver' => 'sqlsrv',
|
||||
'host' => env('DB_HOST', null),
|
||||
'database' => env('DB_DATABASE', null),
|
||||
'username' => env('DB_USERNAME', null),
|
||||
'password' => env('DB_PASSWORD', null),
|
||||
'port' => env('DB_PORT', null),
|
||||
'prefix' => env('DB_PREFIX', null),
|
||||
],
|
||||
|
||||
],
|
||||
@@ -131,6 +131,7 @@ return [
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'port' => env('REDIS_PORT', 6379),
|
||||
'database' => env('REDIS_DATABASE', 0),
|
||||
'password' => env('REDIS_PASSWORD', null),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user