mirror of
https://github.com/cachethq/cachet.git
synced 2026-03-09 18:37:01 +00:00
Compare commits
329 Commits
2.1
...
v2.3.0-RC1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
f79fadab80 | ||
|
|
e3dc7594e5 | ||
|
|
200771cdab | ||
|
|
56760c7445 | ||
|
|
df373e93ef | ||
|
|
65ebfde315 | ||
|
|
4f7c50f7f4 | ||
|
|
de232ab2b5 | ||
|
|
067018cf47 | ||
|
|
97e5fa07fa | ||
|
|
dec48a92f3 | ||
|
|
0672fe046e | ||
|
|
b78009bee4 | ||
|
|
5907f54201 | ||
|
|
555865d761 | ||
|
|
c0561ad8c0 | ||
|
|
28e7af7107 | ||
|
|
f9bc46b460 | ||
|
|
3730ca8811 | ||
|
|
69716730ac | ||
|
|
ef8049c21d | ||
|
|
cf80bf31f3 | ||
|
|
28bf01396c | ||
|
|
cf900e1390 | ||
|
|
df31b4d20e | ||
|
|
a9185b7876 | ||
|
|
f383d50f16 | ||
|
|
369073e636 | ||
|
|
eb6a638a59 | ||
|
|
9a743ab425 | ||
|
|
36d89a9ad8 | ||
|
|
7dfe1b6fab | ||
|
|
ebc6525b65 | ||
|
|
c5ae146683 | ||
|
|
70daa9d3fa | ||
|
|
95d3d427a4 | ||
|
|
8de387a92d | ||
|
|
5e6055c9bd | ||
|
|
c4338aaaeb | ||
|
|
ca06352bda | ||
|
|
edf78d7248 | ||
|
|
d0d40c93d3 | ||
|
|
11730ed932 | ||
|
|
0c4737aca1 | ||
|
|
159390f989 | ||
|
|
5de8521ac8 | ||
|
|
d957474618 | ||
|
|
7c56785fcc | ||
|
|
debc187874 | ||
|
|
6ccac55e1d | ||
|
|
ed77a6753c | ||
|
|
6e7df6e1f7 | ||
|
|
4f867dabe6 | ||
|
|
106a917dbc | ||
|
|
2cf5b9710a | ||
|
|
c60e7e906d | ||
|
|
7521678e5f | ||
|
|
d828e961bd | ||
|
|
fef84f8559 | ||
|
|
39b112dacc | ||
|
|
6054a6509c | ||
|
|
6f3a38d708 | ||
|
|
8e0fa710ee | ||
|
|
309c6d345b | ||
|
|
6a466d2c9c | ||
|
|
7a81d3b6ef | ||
|
|
b0a4820e80 | ||
|
|
fe569a6fbc | ||
|
|
b556eac851 | ||
|
|
84aea0e869 | ||
|
|
841e102971 | ||
|
|
7f65e1e48c | ||
|
|
99a5a85248 | ||
|
|
6cc1eb2c0f | ||
|
|
3d77a7a2cd | ||
|
|
53dd12e339 | ||
|
|
0c3d388f5a | ||
|
|
f73788fab3 | ||
|
|
b5bac7690e | ||
|
|
1438783c07 | ||
|
|
bca7c5340a | ||
|
|
794d3733c1 | ||
|
|
f63f63bb43 | ||
|
|
10c821c466 | ||
|
|
85094805f2 | ||
|
|
adab5487fd | ||
|
|
263933c589 | ||
|
|
120fcf3b37 | ||
|
|
d5542f7555 | ||
|
|
82e4411b45 | ||
|
|
62bba0ffa6 | ||
|
|
f5e45ce938 | ||
|
|
d342cdb6ad | ||
|
|
9b69ed417e | ||
|
|
e689f206cb | ||
|
|
8f80498c2e | ||
|
|
4d07d2c8b1 | ||
|
|
ac31067333 | ||
|
|
c293c39118 | ||
|
|
b8d148e79d | ||
|
|
b29cc6824c | ||
|
|
7d73bc8b23 | ||
|
|
22dbf290b4 | ||
|
|
dcec1e2e95 | ||
|
|
a64b5c1046 | ||
|
|
a5b0b2a1b5 | ||
|
|
6643d417b7 | ||
|
|
9ad20d013b | ||
|
|
37c8e5924f | ||
|
|
ea62a22998 | ||
|
|
fa744b1b97 | ||
|
|
15907a6112 | ||
|
|
b58b6d8375 | ||
|
|
ffa1fdbf1a | ||
|
|
5b646f9618 | ||
|
|
cbff8d50fa | ||
|
|
cc37fcfa78 | ||
|
|
71d6e692eb | ||
|
|
045667dce8 | ||
|
|
93dcf5b68a | ||
|
|
1b383e1d57 | ||
|
|
83c03cc3c7 | ||
|
|
419fa31d6e | ||
|
|
17bb3f66e8 | ||
|
|
97ba43a898 | ||
|
|
51bbcc2a7d | ||
|
|
8e8a78ca10 | ||
|
|
dd062c673f | ||
|
|
4cb57c5f1e | ||
|
|
bf1ef04ce2 | ||
|
|
4a86604bcc | ||
|
|
6610c1a50c | ||
|
|
957d7e5722 | ||
|
|
9844d0cff4 | ||
|
|
141ffeb474 | ||
|
|
31614d0c4d | ||
|
|
3817fc2cc6 | ||
|
|
3a278c1f5b | ||
|
|
6c8a6b2822 | ||
|
|
0d4c7a7e28 | ||
|
|
97b23cd002 | ||
|
|
97ec85ba2a | ||
|
|
cdfaadf986 | ||
|
|
9919853d07 | ||
|
|
a0477b03e3 | ||
|
|
d4d31efeb7 | ||
|
|
5fd3f379fc | ||
|
|
b2fca4c55a | ||
|
|
1a7b97b8b2 | ||
|
|
247016b61d | ||
|
|
13f1e6a713 | ||
|
|
f9e332cac7 | ||
|
|
0b3483fb8a | ||
|
|
9379ab131c | ||
|
|
5fa698f5b7 | ||
|
|
83991c132a | ||
|
|
6e7190b6bd | ||
|
|
9791118581 | ||
|
|
186ea7e8c3 | ||
|
|
3184df7d6c | ||
|
|
06284ecd13 | ||
|
|
4150f8c032 | ||
|
|
8051186de3 | ||
|
|
d9c97d3ef5 | ||
|
|
73d59e082d | ||
|
|
57273dd96f | ||
|
|
b4e023b7ce | ||
|
|
924e74d3c8 | ||
|
|
335d326a73 | ||
|
|
d572a67855 | ||
|
|
328608dfff | ||
|
|
be78216ff3 | ||
|
|
24a29f2251 | ||
|
|
d4655112ca | ||
|
|
f30710c5a8 | ||
|
|
eee94086db | ||
|
|
ca152ecbc0 | ||
|
|
fce4823d2c | ||
|
|
a20b95b87c | ||
|
|
640571a6bc | ||
|
|
2ad0085370 | ||
|
|
053d3191c8 | ||
|
|
6b6f5ee09d | ||
|
|
481296ad21 | ||
|
|
55d4a639d9 | ||
|
|
b19c9fc885 | ||
|
|
a2abd43fd2 | ||
|
|
1ab9d95b9c | ||
|
|
08b490a3cc | ||
|
|
919c7127e7 | ||
|
|
98550c31c9 | ||
|
|
4deff42aff | ||
|
|
e8cbd81ad0 | ||
|
|
54a8fbd185 | ||
|
|
49c6a7806b | ||
|
|
251e90433e | ||
|
|
3f7d1f588b | ||
|
|
9a3b073fc6 | ||
|
|
f92761437d | ||
|
|
f186907d8b | ||
|
|
718bd99c24 | ||
|
|
5c0f39759e | ||
|
|
bc9607a735 | ||
|
|
01e40c4684 | ||
|
|
69fe2d7b3a | ||
|
|
363dd5358f | ||
|
|
3487194415 | ||
|
|
08dc9e0be9 | ||
|
|
781b2238c6 | ||
|
|
e51504b3ec | ||
|
|
af0e578a44 | ||
|
|
140970ac37 | ||
|
|
0b78332a20 | ||
|
|
c9f08a3e6e | ||
|
|
52a1f42426 | ||
|
|
dc1022e6f3 | ||
|
|
63eaaa325e | ||
|
|
819738cc77 | ||
|
|
d096e603d4 | ||
|
|
8d1c244e02 | ||
|
|
37898bee60 | ||
|
|
7b05d1d1b6 | ||
|
|
625b7d9a81 | ||
|
|
cb08443b27 | ||
|
|
026d296d8f | ||
|
|
37af01483a | ||
|
|
726f5e851d | ||
|
|
73fa0951f7 | ||
|
|
4e3879a949 | ||
|
|
7f55829035 | ||
|
|
63dd826377 | ||
|
|
2565774fea |
4
.bowerrc
4
.bowerrc
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"directory": "vendor/bower_components",
|
||||
"interactive": false
|
||||
}
|
||||
@@ -9,10 +9,11 @@ DB_DATABASE=cachet
|
||||
DB_USERNAME=homestead
|
||||
DB_PASSWORD=secret
|
||||
DB_PORT=null
|
||||
DB_PREFIX=null
|
||||
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=database
|
||||
QUEUE_DRIVER=sync
|
||||
CACHET_EMOJI=false
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,3 +1,5 @@
|
||||
* text=auto
|
||||
*.css linguist-vendored
|
||||
*.scss linguist-vendored
|
||||
/.github export-ignore
|
||||
/tests export-ignore
|
||||
|
||||
25
.github/ISSUE_TEMPLATE
vendored
Normal file
25
.github/ISSUE_TEMPLATE
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
Before submitting your issue, please make sure that you've checked all of 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?*
|
||||
|
||||
### Expected behaviour
|
||||
|
||||
*Please describe what you're expecting to see happen.*
|
||||
|
||||
### Actual behaviour
|
||||
|
||||
*Please describe what you're actually seeing happen.*
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
*If your issue requires any specific steps to reproduce, please outline them here.*
|
||||
52
README.md
52
README.md
@@ -1,13 +1,20 @@
|
||||
# Cachet
|
||||
|
||||
[](https://styleci.io/repos/26730195/)
|
||||
[](https://travis-ci.org/CachetHQ/Cachet)
|
||||
[](https://travis-ci.org/CachetHQ/Cachet)
|
||||
[](https://scrutinizer-ci.com/g/CachetHQ/Cachet)
|
||||
[](LICENSE)
|
||||
[](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.
|
||||
|
||||
## Supporting Cachet
|
||||
|
||||
Cachet is a BSD-3-licensed open source project. If you'd like to support future development, check out the [Cachet Patreon campaign](https://patreon.com/jbrooksuk).
|
||||
|
||||
## Features
|
||||
|
||||
- List your services components
|
||||
@@ -28,15 +35,44 @@ The `master` branch of this repository is a development branch and **should not*
|
||||
## Requirements
|
||||
|
||||
- PHP 5.5.9+ or newer
|
||||
- Apache or Nginx server
|
||||
- [Composer](https://getcomposer.org)
|
||||
|
||||
### Development Requirements
|
||||
## How to contribute
|
||||
|
||||
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).
|
||||
|
||||
Of course bug reports, feature requests and [documentation](https://docs.cachethq.io) are always being sought after.
|
||||
|
||||
### Contributing as a designer
|
||||
|
||||
As Cachet gains new features, the design and ideas that were once a perfect fit need updating and in some cases designing from scratch. This is where you come in! Fancy giving Cachet a lick of paint? Sweet!
|
||||
|
||||
You'll need to install Node.js, Bower and Gulp.
|
||||
|
||||
To get started you can do the following:
|
||||
|
||||
1. Install Node.js and our dev dependencies.
|
||||
2. Modify the SCSS files in `./resources/assets/sass/`
|
||||
3. Run `gulp`
|
||||
|
||||
If you're making a lot of changes, you'll find that running `gulp watch` will really help you out!
|
||||
|
||||
### 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!
|
||||
|
||||
These extra dependencies are required 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.
|
||||
|
||||
## Installation, Upgrades and Documentation
|
||||
|
||||
@@ -55,6 +91,10 @@ To test out the demo, you may login to the [Dashboard](https://demo.cachethq.io/
|
||||
|
||||
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.
|
||||
|
||||
## 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).
|
||||
@@ -62,3 +102,9 @@ A special thank you to our [translators](https://crowdin.com/project/cachet/acti
|
||||
## 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
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
|
||||
namespace CachetHQ\Cachet\Bus\Commands\ComponentGroup;
|
||||
|
||||
/**
|
||||
* This is the add component group command.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
final class AddComponentGroupCommand
|
||||
{
|
||||
/**
|
||||
@@ -30,7 +35,7 @@ final class AddComponentGroupCommand
|
||||
/**
|
||||
* Is the component group collapsed?
|
||||
*
|
||||
* @var bool
|
||||
* @var int
|
||||
*/
|
||||
public $collapsed;
|
||||
|
||||
@@ -42,7 +47,7 @@ final class AddComponentGroupCommand
|
||||
public $rules = [
|
||||
'name' => 'required|string',
|
||||
'order' => 'int',
|
||||
'collapsed' => 'bool',
|
||||
'collapsed' => 'int|between:0,3',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -50,7 +55,7 @@ final class AddComponentGroupCommand
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $order
|
||||
* @param bool $collapsed
|
||||
* @param int $collapsed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,11 @@ namespace CachetHQ\Cachet\Bus\Commands\ComponentGroup;
|
||||
|
||||
use CachetHQ\Cachet\Models\ComponentGroup;
|
||||
|
||||
/**
|
||||
* This is the update component group command.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
final class UpdateComponentGroupCommand
|
||||
{
|
||||
/**
|
||||
@@ -39,7 +44,7 @@ final class UpdateComponentGroupCommand
|
||||
/**
|
||||
* Is the component group collapsed?
|
||||
*
|
||||
* @var bool
|
||||
* @var int
|
||||
*/
|
||||
public $collapsed;
|
||||
|
||||
@@ -51,7 +56,7 @@ final class UpdateComponentGroupCommand
|
||||
public $rules = [
|
||||
'name' => 'string',
|
||||
'order' => 'int',
|
||||
'collapsed' => 'bool',
|
||||
'collapsed' => 'int|between:0,3',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -60,7 +65,7 @@ final class UpdateComponentGroupCommand
|
||||
* @param \CachetHQ\Cachet\Models\ComponentGroup $group
|
||||
* @param string $name
|
||||
* @param int $order
|
||||
* @param bool $collapsed
|
||||
* @param int $collapsed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -69,6 +69,13 @@ final class AddMetricCommand
|
||||
*/
|
||||
public $default_view;
|
||||
|
||||
/**
|
||||
* The threshold to buffer the metric points in.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $threshold;
|
||||
|
||||
/**
|
||||
* The validation rules.
|
||||
*
|
||||
@@ -84,6 +91,7 @@ final class AddMetricCommand
|
||||
'display_chart' => 'int',
|
||||
'places' => 'int|between:0,4',
|
||||
'default_view' => 'int|between:0,3',
|
||||
'threshold' => 'numeric|between:0,10',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -97,10 +105,11 @@ final class AddMetricCommand
|
||||
* @param int $display_chart
|
||||
* @param int $places
|
||||
* @param int $default_view
|
||||
* @param int $threshold
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->suffix = $suffix;
|
||||
@@ -110,5 +119,6 @@ final class AddMetricCommand
|
||||
$this->display_chart = $display_chart;
|
||||
$this->places = $places;
|
||||
$this->default_view = $default_view;
|
||||
$this->threshold = $threshold;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,13 @@ final class UpdateMetricCommand
|
||||
*/
|
||||
public $default_view;
|
||||
|
||||
/**
|
||||
* The threshold to buffer the metric points in.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $threshold;
|
||||
|
||||
/**
|
||||
* The validation rules.
|
||||
*
|
||||
@@ -93,6 +100,7 @@ final class UpdateMetricCommand
|
||||
'display_chart' => 'int',
|
||||
'places' => 'numeric|between:0,4',
|
||||
'default_view' => 'numeric|between:0,4',
|
||||
'threshold' => 'numeric|between:0,10',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -107,10 +115,11 @@ final class UpdateMetricCommand
|
||||
* @param int $display_chart
|
||||
* @param int $places
|
||||
* @param int $default_view
|
||||
* @param int $threshold
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
$this->metric = $metric;
|
||||
$this->name = $name;
|
||||
@@ -121,5 +130,6 @@ final class UpdateMetricCommand
|
||||
$this->display_chart = $display_chart;
|
||||
$this->places = $places;
|
||||
$this->default_view = $default_view;
|
||||
$this->threshold = $threshold;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -18,8 +18,13 @@ use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\Incident;
|
||||
use CachetHQ\Cachet\Models\IncidentTemplate;
|
||||
use Twig_Loader_String;
|
||||
use TwigBridge\Facade\Twig;
|
||||
use TwigBridge\Bridge;
|
||||
|
||||
/**
|
||||
* This is the report incident command handler.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
class ReportIncidentCommandHandler
|
||||
{
|
||||
/**
|
||||
@@ -29,16 +34,25 @@ class ReportIncidentCommandHandler
|
||||
*/
|
||||
protected $dates;
|
||||
|
||||
/**
|
||||
* The twig bridge instance.
|
||||
*
|
||||
* @var \TwigBridge\Bridge
|
||||
*/
|
||||
protected $twig;
|
||||
|
||||
/**
|
||||
* Create a new report incident command handler instance.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Dates\DateFactory $dates
|
||||
* @param \TwigBridge\Bridge $twig
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(DateFactory $dates)
|
||||
public function __construct(DateFactory $dates, Bridge $twig)
|
||||
{
|
||||
$this->dates = $dates;
|
||||
$this->twig = $twig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,9 +120,9 @@ class ReportIncidentCommandHandler
|
||||
$vars = [];
|
||||
}
|
||||
|
||||
Twig::setLoader(new Twig_Loader_String());
|
||||
$this->twig->setLoader(new Twig_Loader_String());
|
||||
$template = IncidentTemplate::forSlug($templateSlug)->first();
|
||||
|
||||
return Twig::render($template->template, $vars);
|
||||
return $this->twig->render($template->template, $vars);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,13 @@ use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\Incident;
|
||||
use CachetHQ\Cachet\Models\IncidentTemplate;
|
||||
use Twig_Loader_String;
|
||||
use TwigBridge\Facade\Twig;
|
||||
use TwigBridge\Bridge;
|
||||
|
||||
/**
|
||||
* This is the update incident command handler.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
class UpdateIncidentCommandHandler
|
||||
{
|
||||
/**
|
||||
@@ -29,16 +34,25 @@ class UpdateIncidentCommandHandler
|
||||
*/
|
||||
protected $dates;
|
||||
|
||||
/**
|
||||
* The twig bridge instance.
|
||||
*
|
||||
* @var \TwigBridge\Bridge
|
||||
*/
|
||||
protected $twig;
|
||||
|
||||
/**
|
||||
* Create a new update incident command handler instance.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Dates\DateFactory $dates
|
||||
* @param \TwigBridge\Bridge $twig
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(DateFactory $dates)
|
||||
public function __construct(DateFactory $dates, Bridge $twig)
|
||||
{
|
||||
$this->dates = $dates;
|
||||
$this->twig = $twig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,9 +127,9 @@ class UpdateIncidentCommandHandler
|
||||
*/
|
||||
protected function parseIncidentTemplate($templateSlug, $vars)
|
||||
{
|
||||
Twig::setLoader(new Twig_Loader_String());
|
||||
$this->twig->setLoader(new Twig_Loader_String());
|
||||
$template = IncidentTemplate::forSlug($templateSlug)->first();
|
||||
|
||||
return Twig::render($template->template, $vars);
|
||||
return $this->twig->render($template->template, $vars);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ class AddMetricCommandHandler
|
||||
'display_chart' => $command->display_chart,
|
||||
'places' => $command->places,
|
||||
'default_view' => $command->default_view,
|
||||
'threshold' => $command->threshold,
|
||||
]);
|
||||
|
||||
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,7 @@ class UpdateMetricCommandHandler
|
||||
'display_chart' => $command->display_chart,
|
||||
'places' => $command->places,
|
||||
'default_view' => $command->default_view,
|
||||
'threshold' => $command->threshold,
|
||||
];
|
||||
|
||||
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,35 +32,34 @@ class SubscribeSubscriberCommandHandler
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand $command
|
||||
*
|
||||
* @throws \CachetHQ\Cachet\Exceptions\AlreadySubscribedException
|
||||
*
|
||||
* @return \CachetHQ\Cachet\Models\Subscriber
|
||||
*/
|
||||
public function handle(SubscribeSubscriberCommand $command)
|
||||
{
|
||||
if (Subscriber::where('email', $command->email)->first() && $command->subscriptions === null) {
|
||||
throw new AlreadySubscribedException("Cannot subscribe {$command->email} because they're already subscribed.");
|
||||
if ($subscriber = Subscriber::where('email', $command->email)->first()) {
|
||||
return $subscriber;
|
||||
}
|
||||
|
||||
$subscriber = Subscriber::firstOrCreate(['email' => $command->email]);
|
||||
|
||||
// Decide what to subscribe the subscriber to.
|
||||
if ($subscriptions = $command->subscriptions) {
|
||||
foreach ($subscriptions as $subscription => $subscriptionValue) {
|
||||
Subscription::firstOrCreate([
|
||||
'subscriber_id' => $subscriber->id,
|
||||
$subscription => $subscriptionValue,
|
||||
]);
|
||||
}
|
||||
$subscriptions = Component::whereIn('id', $subscriptions);
|
||||
} else {
|
||||
$subscriptions = Component::all();
|
||||
}
|
||||
|
||||
if ($subscriber->is_verified === false) {
|
||||
if ($command->verified) {
|
||||
dispatch(new VerifySubscriberCommand($subscriber));
|
||||
} else {
|
||||
event(new SubscriberHasSubscribedEvent($subscriber));
|
||||
}
|
||||
foreach ($subscriptions as $component) {
|
||||
Subscription::create([
|
||||
'subscriber_id' => $subscriber->id,
|
||||
'component_id' => $component->id,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($command->verified) {
|
||||
dispatch(new VerifySubscriberCommand($subscriber));
|
||||
} else {
|
||||
event(new SubscriberHasUpdatedSubscriptionsEvent($subscriber));
|
||||
event(new SubscriberHasSubscribedEvent($subscriber));
|
||||
}
|
||||
|
||||
return $subscriber;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,23 +59,53 @@ class SendComponentUpdateEmailNotificationHandler
|
||||
{
|
||||
$component = AutoPresenter::decorate($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)
|
||||
{
|
||||
$mail = [
|
||||
'subject' => trans('cachet.subscriber.email.component.subject'),
|
||||
'component_name' => $component->name,
|
||||
'component_human_status' => $component->human_status,
|
||||
];
|
||||
|
||||
foreach (Subscription::isVerifiedForComponent($component->id)->with('subscriber')->get() as $subscription) {
|
||||
$subscriber = $subscription->subscriber;
|
||||
$mail['email'] = $subscriber->email;
|
||||
$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,31 +60,69 @@ 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,
|
||||
'unsubscribe_link' => route('subscribe.unsubscribe', ['code' => $subscriber->verify_code]),
|
||||
];
|
||||
$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]),
|
||||
];
|
||||
|
||||
$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,26 +60,69 @@ 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,
|
||||
'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' => 'Scheduled maintenance.',
|
||||
'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]),
|
||||
];
|
||||
|
||||
$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']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class AppComposer
|
||||
{
|
||||
$view->withAboutApp(Markdown::convertToHtml(Config::get('setting.app_about')));
|
||||
$view->withAppAnalytics(Config::get('setting.app_analytics'));
|
||||
$view->withAppAnalyticsGoSquared(Config::get('setting.app_analytics_go_squared'));
|
||||
$view->withAppAnalyticsGoSquared(Config::get('setting.app_analytics_gs'));
|
||||
$view->withAppAnalyticsPiwikUrl(Config::get('setting.app_analytics_piwik_url'));
|
||||
$view->withAppAnalyticsPiwikSiteId(Config::get('setting.app_analytics_piwik_site_id'));
|
||||
$view->withAppBanner(Config::get('setting.app_banner'));
|
||||
@@ -39,14 +39,19 @@ class AppComposer
|
||||
$view->withAppLocale(Config::get('setting.app_locale'));
|
||||
$view->withAppStylesheet(Config::get('setting.stylesheet'));
|
||||
$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'));
|
||||
$view->withAutomaticLocalization(Config::get('setting.automatic_localization'));
|
||||
|
||||
if ($support) {
|
||||
$view->withSiteTitle(Config::get('setting.app_name').' | Cachet');
|
||||
} else {
|
||||
$view->withSiteTitle(Config::get('setting.app_name'));
|
||||
}
|
||||
|
||||
$view->withFontSubset(Config::get('langs.'.Config::get('app.locale').'.subset', 'latin'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace CachetHQ\Cachet\Composers;
|
||||
|
||||
use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\Incident;
|
||||
use CachetHQ\Cachet\Models\IncidentTemplate;
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
class DashboardComposer
|
||||
@@ -27,6 +29,8 @@ class DashboardComposer
|
||||
public function compose(View $view)
|
||||
{
|
||||
$view->withIncidentCount(Incident::notScheduled()->count());
|
||||
$view->withIncidentTemplateCount(IncidentTemplate::count());
|
||||
$view->withComponentCount(Component::all()->count());
|
||||
$view->withSubscriberCount(Subscriber::isVerified()->count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,29 +12,11 @@
|
||||
namespace CachetHQ\Cachet\Composers;
|
||||
|
||||
use CachetHQ\Cachet\Models\Metric;
|
||||
use CachetHQ\Cachet\Repositories\Metric\MetricRepository;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
class MetricsComposer
|
||||
{
|
||||
/**
|
||||
* @var \CachetHQ\Cachet\Repositories\Metric\MetricRepository
|
||||
*/
|
||||
protected $metricRepository;
|
||||
|
||||
/**
|
||||
* Construct a new home controller instance.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Repositories\Metric\MetricRepository $metricRepository
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(MetricRepository $metricRepository)
|
||||
{
|
||||
$this->metricRepository = $metricRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metrics view composer.
|
||||
*
|
||||
@@ -45,22 +27,11 @@ class MetricsComposer
|
||||
public function compose(View $view)
|
||||
{
|
||||
$metrics = null;
|
||||
$metricData = [];
|
||||
if ($displayMetrics = Config::get('setting.display_graphs')) {
|
||||
$metrics = Metric::where('display_chart', 1)->orderBy('id')->get();
|
||||
|
||||
$metrics->map(function ($metric) use (&$metricData) {
|
||||
$metricData[$metric->id] = [
|
||||
'last_hour' => $this->metricRepository->listPointsLastHour($metric),
|
||||
'today' => $this->metricRepository->listPointsToday($metric),
|
||||
'week' => $this->metricRepository->listPointsForWeek($metric),
|
||||
'month' => $this->metricRepository->listPointsForMonth($metric),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
$view->withDisplayMetrics($displayMetrics)
|
||||
->withMetrics($metrics)
|
||||
->withMetricData($metricData);
|
||||
->withMetrics($metrics);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace CachetHQ\Cachet\Console\Commands;
|
||||
use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\ComponentGroup;
|
||||
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;
|
||||
@@ -62,6 +63,7 @@ class DemoSeederCommand extends Command
|
||||
$this->seedComponentGroups();
|
||||
$this->seedComponents();
|
||||
$this->seedIncidents();
|
||||
$this->seedIncidentTemplates();
|
||||
$this->seedMetricPoints();
|
||||
$this->seedMetrics();
|
||||
$this->seedSettings();
|
||||
@@ -82,11 +84,11 @@ class DemoSeederCommand extends Command
|
||||
[
|
||||
'name' => 'Websites',
|
||||
'order' => 1,
|
||||
'collapsed' => false,
|
||||
'collapsed' => 0,
|
||||
], [
|
||||
'name' => 'Alt Three',
|
||||
'order' => 2,
|
||||
'collapsed' => true,
|
||||
'collapsed' => 1,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -157,7 +159,25 @@ class DemoSeederCommand extends Command
|
||||
*/
|
||||
protected function seedIncidents()
|
||||
{
|
||||
$incidentMessage = <<<'EINCIDENT'
|
||||
# Of course it does!
|
||||
|
||||
What kind of web application doesn't these days?
|
||||
|
||||
## Headers are fun aren't they
|
||||
|
||||
It's _exactly_ why we need Markdown. For **emphasis** and such.
|
||||
EINCIDENT;
|
||||
|
||||
$defaultIncidents = [
|
||||
[
|
||||
'name' => 'Cachet supports Markdown!',
|
||||
'message' => $incidentMessage,
|
||||
'status' => 4,
|
||||
'component_id' => 0,
|
||||
'scheduled_at' => null,
|
||||
'visible' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'Awesome',
|
||||
'message' => ':+1: We totally nailed the fix.',
|
||||
@@ -207,6 +227,16 @@ class DemoSeederCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seed the incident templates table.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function seedIncidentTemplates()
|
||||
{
|
||||
IncidentTemplate::truncate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Seed the metric points table.
|
||||
*
|
||||
@@ -265,38 +295,33 @@ class DemoSeederCommand extends Command
|
||||
[
|
||||
'name' => 'app_name',
|
||||
'value' => 'Cachet Demo',
|
||||
],
|
||||
[
|
||||
], [
|
||||
'name' => 'app_domain',
|
||||
'value' => 'https://demo.cachethq.io',
|
||||
],
|
||||
[
|
||||
], [
|
||||
'name' => 'show_support',
|
||||
'value' => '1',
|
||||
],
|
||||
[
|
||||
], [
|
||||
'name' => 'app_locale',
|
||||
'value' => 'en',
|
||||
],
|
||||
[
|
||||
], [
|
||||
'name' => 'app_timezone',
|
||||
'value' => 'Europe/London',
|
||||
],
|
||||
[
|
||||
], [
|
||||
'name' => 'app_incident_days',
|
||||
'value' => '7',
|
||||
],
|
||||
[
|
||||
], [
|
||||
'name' => 'app_analytics',
|
||||
'value' => 'UA-58442674-3',
|
||||
],
|
||||
[
|
||||
], [
|
||||
'name' => 'app_analytics_gs',
|
||||
'value' => 'GSN-712462-P',
|
||||
],
|
||||
[
|
||||
], [
|
||||
'name' => 'display_graphs',
|
||||
'value' => '1',
|
||||
], [
|
||||
'name' => '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.',
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -12,10 +12,19 @@
|
||||
namespace CachetHQ\Cachet\Foundation\Providers;
|
||||
|
||||
use AltThree\Bus\Dispatcher;
|
||||
use CachetHQ\Cachet\Bus\Middleware\UseDatabaseTransactions;
|
||||
use CachetHQ\Cachet\Dates\DateFactory;
|
||||
use CachetHQ\Cachet\GitHub\Release;
|
||||
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 +38,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 +53,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
public function register()
|
||||
{
|
||||
$this->registerDateFactory();
|
||||
$this->registerRelease();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,10 +64,25 @@ 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 releases class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerRelease()
|
||||
{
|
||||
$this->app->singleton(Release::class, function ($app) {
|
||||
$cache = $app['cache.store'];
|
||||
$token = $app['config']->get('services.github.token');
|
||||
|
||||
return new Release($cache, $token);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ class ComposerServiceProvider extends ServiceProvider
|
||||
$factory->composer('*', AppComposer::class);
|
||||
$factory->composer('*', CurrentUserComposer::class);
|
||||
$factory->composer(['index'], MetricsComposer::class);
|
||||
$factory->composer(['index', 'incident', 'subscribe', 'signup'], StatusPageComposer::class);
|
||||
$factory->composer(['index', 'incident', 'subscribe', 'signup', 'dashboard.settings.theme'], ThemeComposer::class);
|
||||
$factory->composer(['index', 'single-incident', 'subscribe', 'signup'], StatusPageComposer::class);
|
||||
$factory->composer(['index', 'single-incident', 'subscribe.*', 'signup', 'dashboard.settings.theme'], ThemeComposer::class);
|
||||
$factory->composer('dashboard.*', DashboardComposer::class);
|
||||
$factory->composer(['setup', 'dashboard.settings.localization'], TimezoneLocaleComposer::class);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,19 @@
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 +33,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) {
|
||||
//
|
||||
}
|
||||
@@ -65,10 +91,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,7 +34,7 @@ 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',
|
||||
|
||||
@@ -17,7 +17,7 @@ use Illuminate\Routing\Router;
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* This namespace is applied to the controller routes in your routes file.
|
||||
* This namespace is applied to your controller routes.
|
||||
*
|
||||
* In addition, it is set as the URL generator's root namespace.
|
||||
*
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace CachetHQ\Cachet\GitHub;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Contracts\Cache\Repository as CacheRepository;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
|
||||
class Release
|
||||
{
|
||||
@@ -25,24 +24,24 @@ class Release
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The config repository instance.
|
||||
* The github authentication token.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
* @var string
|
||||
*/
|
||||
protected $config;
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* Creates a new release instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @param string $token
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(CacheRepository $cache, ConfigRepository $config)
|
||||
public function __construct(CacheRepository $cache, $token)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->config = $config;
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,8 +55,8 @@ class Release
|
||||
$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;
|
||||
if ($this->token) {
|
||||
$headers['OAUTH-TOKEN'] = $this->token;
|
||||
}
|
||||
|
||||
return json_decode((new Client())->get('https://api.github.com/repos/cachethq/cachet/releases/latest', [
|
||||
|
||||
@@ -161,12 +161,6 @@ abstract class AbstractApiController extends Controller
|
||||
|
||||
$items = $paginator->getCollection();
|
||||
|
||||
if ($sortBy = $request->get('sort')) {
|
||||
$direction = $request->has('order') && $request->get('order') == 'desc';
|
||||
|
||||
$items = $items->sortBy($sortBy, SORT_REGULAR, $direction);
|
||||
}
|
||||
|
||||
return $this->setMetaData($pagination)->setData(AutoPresenter::decorate($items->values()))->respond();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,22 @@ class ComponentController extends AbstractApiController
|
||||
public function getComponents()
|
||||
{
|
||||
if (app(Guard::class)->check()) {
|
||||
$components = Component::whereRaw('1 = 1');
|
||||
$components = Component::query();
|
||||
} else {
|
||||
$components = Component::enabled();
|
||||
}
|
||||
|
||||
return $this->paginator($components->paginate(Binput::get('per_page', 20)), Request::instance());
|
||||
$components->search(Binput::except(['sort', 'order', 'per_page']));
|
||||
|
||||
if ($sortBy = Binput::get('sort')) {
|
||||
$direction = Binput::has('order') && Binput::get('order') == 'desc';
|
||||
|
||||
$components->sort($sortBy, $direction);
|
||||
}
|
||||
|
||||
$components = $components->paginate(Binput::get('per_page', 20));
|
||||
|
||||
return $this->paginator($components, Request::instance());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/**
|
||||
@@ -29,7 +36,17 @@ class ComponentGroupController extends AbstractApiController
|
||||
*/
|
||||
public function getGroups()
|
||||
{
|
||||
$groups = ComponentGroup::paginate(Binput::get('per_page', 20));
|
||||
$groups = ComponentGroup::query();
|
||||
|
||||
$groups->search(Binput::except(['sort', 'order', 'per_page']));
|
||||
|
||||
if ($sortBy = Binput::get('sort')) {
|
||||
$direction = Binput::has('order') && Binput::get('order') == 'desc';
|
||||
|
||||
$groups->sort($sortBy, $direction);
|
||||
}
|
||||
|
||||
$groups = $groups->paginate(Binput::get('per_page', 20));
|
||||
|
||||
return $this->paginator($groups, Request::instance());
|
||||
}
|
||||
@@ -57,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();
|
||||
@@ -79,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) {
|
||||
|
||||
@@ -22,4 +22,14 @@ class GeneralController extends AbstractApiController
|
||||
{
|
||||
return $this->item('Pong!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint to show the Cachet version.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function version()
|
||||
{
|
||||
return $this->item(CACHET_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,17 @@ class IncidentController extends AbstractApiController
|
||||
{
|
||||
$incidentVisibility = app(Guard::class)->check() ? 0 : 1;
|
||||
|
||||
$incidents = Incident::where('visible', '>=', $incidentVisibility)->paginate(Binput::get('per_page', 20));
|
||||
$incidents = Incident::where('visible', '>=', $incidentVisibility);
|
||||
|
||||
$incidents->search(Binput::except(['sort', 'order', 'per_page']));
|
||||
|
||||
if ($sortBy = Binput::get('sort')) {
|
||||
$direction = Binput::has('order') && Binput::get('order') == 'desc';
|
||||
|
||||
$incidents->sort($sortBy, $direction);
|
||||
}
|
||||
|
||||
$incidents = $incidents->paginate(Binput::get('per_page', 20));
|
||||
|
||||
return $this->paginator($incidents, Request::instance());
|
||||
}
|
||||
|
||||
@@ -29,7 +29,15 @@ class MetricController extends AbstractApiController
|
||||
*/
|
||||
public function getMetrics()
|
||||
{
|
||||
$metrics = Metric::paginate(Binput::get('per_page', 20));
|
||||
$metrics = Metric::query();
|
||||
|
||||
if ($sortBy = Binput::get('sort')) {
|
||||
$direction = Binput::has('order') && Binput::get('order') == 'desc';
|
||||
|
||||
$metrics->sort($sortBy, $direction);
|
||||
}
|
||||
|
||||
$metrics = $metrics->paginate(Binput::get('per_page', 20));
|
||||
|
||||
return $this->paginator($metrics, Request::instance());
|
||||
}
|
||||
@@ -55,7 +63,9 @@ class MetricController extends AbstractApiController
|
||||
*/
|
||||
public function getMetricPoints(Metric $metric)
|
||||
{
|
||||
return $this->collection($metric->points);
|
||||
$points = $metric->points()->paginate(Binput::get('per_page', 20));
|
||||
|
||||
return $this->paginator($points, Request::instance());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,9 +82,10 @@ class MetricController extends AbstractApiController
|
||||
Binput::get('description'),
|
||||
Binput::get('default_value'),
|
||||
Binput::get('calc_type', 0),
|
||||
Binput::get('display_chart'),
|
||||
Binput::get('display_chart', true),
|
||||
Binput::get('places', 2),
|
||||
Binput::get('view', 1)
|
||||
Binput::get('view', 1),
|
||||
Binput::get('threshold', 5)
|
||||
));
|
||||
} catch (QueryException $e) {
|
||||
throw new BadRequestHttpException();
|
||||
@@ -99,10 +110,11 @@ class MetricController extends AbstractApiController
|
||||
Binput::get('suffix'),
|
||||
Binput::get('description'),
|
||||
Binput::get('default_value'),
|
||||
Binput::get('calc_type', 0),
|
||||
Binput::get('calc_type'),
|
||||
Binput::get('display_chart'),
|
||||
Binput::get('places', 2),
|
||||
Binput::get('view', 1)
|
||||
Binput::get('places'),
|
||||
Binput::get('view'),
|
||||
Binput::get('threshold', 5)
|
||||
));
|
||||
} 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();
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ class ComponentController extends Controller
|
||||
$group = dispatch(new UpdateComponentGroupCommand(
|
||||
$group,
|
||||
Binput::get('name'),
|
||||
Binput::get('order', 0),
|
||||
$group->order,
|
||||
Binput::get('collapsed')
|
||||
));
|
||||
} catch (ValidationException $e) {
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -95,10 +95,10 @@ class ScheduleController extends Controller
|
||||
{
|
||||
try {
|
||||
$incident = dispatch(new ReportMaintenanceCommand(
|
||||
Binput::get('incident.name'),
|
||||
Binput::get('incident.message'),
|
||||
Binput::get('incident.notify'),
|
||||
Binput::get('incident.scheduled_at')
|
||||
Binput::get('name'),
|
||||
Binput::get('message'),
|
||||
Binput::get('notify'),
|
||||
Binput::get('scheduled_at')
|
||||
));
|
||||
} catch (ValidationException $e) {
|
||||
return Redirect::route('dashboard.schedule.add')
|
||||
@@ -140,7 +140,7 @@ class ScheduleController extends Controller
|
||||
$scheduleData = Binput::get('incident');
|
||||
|
||||
// Parse the schedule date.
|
||||
$scheduledAt = app(DateFactory::class)->createNormalized('d/m/Y H:i', $scheduleData['scheduled_at']);
|
||||
$scheduledAt = app(DateFactory::class)->create('d/m/Y H:i', $scheduleData['scheduled_at']);
|
||||
|
||||
if ($scheduledAt->isPast()) {
|
||||
$messageBag = new MessageBag();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace CachetHQ\Cachet\Http\Controllers\Dashboard;
|
||||
|
||||
use CachetHQ\Cachet\Models\User;
|
||||
use CachetHQ\Cachet\Settings\Repository;
|
||||
use Exception;
|
||||
use GrahamCampbell\Binput\Facades\Binput;
|
||||
use Illuminate\Routing\Controller;
|
||||
@@ -20,6 +21,7 @@ use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
@@ -56,6 +58,12 @@ class SettingsController extends Controller
|
||||
'icon' => 'ion-paintbucket',
|
||||
'active' => false,
|
||||
],
|
||||
'customization' => [
|
||||
'title' => trans('dashboard.settings.customization.customization'),
|
||||
'url' => route('dashboard.settings.customization'),
|
||||
'icon' => 'ion-wand',
|
||||
'active' => false,
|
||||
],
|
||||
'localization' => [
|
||||
'title' => trans('dashboard.settings.localization.localization'),
|
||||
'url' => route('dashboard.settings.localization'),
|
||||
@@ -137,6 +145,22 @@ class SettingsController extends Controller
|
||||
->withSubMenu($this->subMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the settings customization view.
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showCustomizationView()
|
||||
{
|
||||
$this->subMenu['customization']['active'] = true;
|
||||
|
||||
Session::flash('redirect_to', $this->subMenu['customization']['url']);
|
||||
|
||||
return View::make('dashboard.settings.customization')
|
||||
->withPageTitle(trans('dashboard.settings.customization.customization').' - '.trans('dashboard.dashboard'))
|
||||
->withSubMenu($this->subMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the settings theme view.
|
||||
*
|
||||
@@ -197,12 +221,30 @@ class SettingsController extends Controller
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
$parameters = Binput::all();
|
||||
|
||||
if (isset($parameters['header'])) {
|
||||
if ($header = Binput::get('header', null, false, false)) {
|
||||
$setting->set('header', $header);
|
||||
} else {
|
||||
$setting->delete('header');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($parameters['footer'])) {
|
||||
if ($footer = Binput::get('footer', null, false, false)) {
|
||||
$setting->set('footer', $footer);
|
||||
} else {
|
||||
$setting->delete('footer');
|
||||
}
|
||||
}
|
||||
|
||||
if (Binput::hasFile('app_banner')) {
|
||||
$file = Binput::file('app_banner');
|
||||
|
||||
@@ -218,7 +260,7 @@ class SettingsController extends Controller
|
||||
return Redirect::to($redirectUrl)->withErrors($file->getErrorMessage());
|
||||
}
|
||||
|
||||
if (!starts_with($file->getMimeType(), 'image/')) {
|
||||
if (!Str::startsWith($file->getMimeType(), 'image/')) {
|
||||
return Redirect::to($redirectUrl)->withErrors(trans('dashboard.settings.app-setup.images-only'));
|
||||
}
|
||||
|
||||
@@ -229,8 +271,16 @@ class SettingsController extends Controller
|
||||
$setting->set('app_banner_type', $file->getMimeType());
|
||||
}
|
||||
|
||||
$excludedParams = [
|
||||
'_token',
|
||||
'app_banner',
|
||||
'remove_banner',
|
||||
'header',
|
||||
'footer',
|
||||
];
|
||||
|
||||
try {
|
||||
foreach (Binput::except(['app_banner', 'remove_banner', '_token']) as $settingName => $settingValue) {
|
||||
foreach (Binput::except($excludedParams) as $settingName => $settingValue) {
|
||||
if ($settingName === 'app_analytics_pi_url') {
|
||||
$settingValue = rtrim($settingValue, '/');
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -79,7 +79,7 @@ class FeedController extends Controller
|
||||
private function feedAction(ComponentGroup &$group, $isRss)
|
||||
{
|
||||
if ($group->exists) {
|
||||
$group->components->map(function ($component) {
|
||||
$group->components->map(function ($component) use ($isRss) {
|
||||
$component->incidents()->visible()->orderBy('created_at', 'desc')->get()->map(function ($incident) use ($isRss) {
|
||||
$this->feedAddItem($incident, $isRss);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
@@ -96,11 +97,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');
|
||||
|
||||
@@ -180,7 +176,7 @@ class SetupController extends Controller
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
$setting = app('setting');
|
||||
$setting = app(Repository::class);
|
||||
|
||||
$settings = array_pull($postData, 'settings');
|
||||
|
||||
@@ -235,26 +231,4 @@ class SetupController extends Controller
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,18 +11,42 @@
|
||||
|
||||
namespace CachetHQ\Cachet\Http\Controllers;
|
||||
|
||||
use AltThree\Badger\Facades\Badger;
|
||||
use CachetHQ\Cachet\Dates\DateFactory;
|
||||
use CachetHQ\Cachet\Http\Controllers\Api\AbstractApiController;
|
||||
use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\Incident;
|
||||
use CachetHQ\Cachet\Models\Metric;
|
||||
use CachetHQ\Cachet\Repositories\Metric\MetricRepository;
|
||||
use Exception;
|
||||
use GrahamCampbell\Binput\Facades\Binput;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Jenssegers\Date\Date;
|
||||
use McCool\LaravelAutoPresenter\Facades\AutoPresenter;
|
||||
|
||||
class StatusPageController extends Controller
|
||||
class StatusPageController extends AbstractApiController
|
||||
{
|
||||
/**
|
||||
* @var \CachetHQ\Cachet\Repositories\Metric\MetricRepository
|
||||
*/
|
||||
protected $metricRepository;
|
||||
|
||||
/**
|
||||
* Construct a new status page controller instance.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Repositories\Metric\MetricRepository $metricRepository
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(MetricRepository $metricRepository)
|
||||
{
|
||||
$this->metricRepository = $metricRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the status page.
|
||||
*
|
||||
@@ -97,7 +121,77 @@ class StatusPageController extends Controller
|
||||
*/
|
||||
public function showIncident(Incident $incident)
|
||||
{
|
||||
return View::make('incident')
|
||||
return View::make('single-incident')
|
||||
->withIncident($incident);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metrics in a readily formatted way.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Models\Metric $metric
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getMetrics(Metric $metric)
|
||||
{
|
||||
$metricData = [];
|
||||
$type = Binput::get('filter', 'last_hour');
|
||||
|
||||
switch ($type) {
|
||||
case 'last_hour':
|
||||
$metricData = $this->metricRepository->listPointsLastHour($metric);
|
||||
break;
|
||||
case 'today':
|
||||
$metricData = $this->metricRepository->listPointsToday($metric);
|
||||
break;
|
||||
case 'week':
|
||||
$metricData = $this->metricRepository->listPointsForWeek($metric);
|
||||
break;
|
||||
case 'month':
|
||||
$metricData = $this->metricRepository->listPointsForMonth($metric);
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->item([
|
||||
'metric' => $metric->toArray(),
|
||||
'items' => $metricData,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a Shield (badge) for the component.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Models\Component $component
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function showComponentBadge(Component $component)
|
||||
{
|
||||
$component = AutoPresenter::decorate($component);
|
||||
$color = null;
|
||||
|
||||
switch ($component->status_color) {
|
||||
case 'reds':
|
||||
$color = Config::get('setting.style_reds', '#ff6f6f');
|
||||
break;
|
||||
case 'blues':
|
||||
$color = Config::get('setting.style_blues', '#3498db');
|
||||
break;
|
||||
case 'greens':
|
||||
$color = Config::get('setting.style_greens', '#7ED321');
|
||||
break;
|
||||
case 'yellows':
|
||||
$color = Config::get('setting.style_yellows', '#F7CA18');
|
||||
break;
|
||||
}
|
||||
|
||||
$badge = Badger::generate(
|
||||
$component->name,
|
||||
$component->human_status,
|
||||
substr($color, 1),
|
||||
Binput::get('style', 'flat-square')
|
||||
);
|
||||
|
||||
return Response::make($badge, 200, ['Content-Type' => 'image/svg+xml']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -42,7 +43,7 @@ class SubscribeController extends Controller
|
||||
*/
|
||||
public function showSubscribe()
|
||||
{
|
||||
return View::make('subscribe')
|
||||
return View::make('subscribe.subscribe')
|
||||
->withAboutApp(Markdown::convertToHtml(Config::get('setting.app_about')));
|
||||
}
|
||||
|
||||
@@ -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')))
|
||||
->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('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), $message));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,11 +91,13 @@ class SubscribeController extends Controller
|
||||
|
||||
$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')));
|
||||
@@ -127,4 +132,63 @@ class SubscribeController extends Controller
|
||||
return Redirect::route('status-page')
|
||||
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.unsubscribed')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the subscription manager page.
|
||||
*
|
||||
* @param string|null $code
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showManage($code = null)
|
||||
{
|
||||
if ($code === null) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$subscriber = Subscriber::where('verify_code', '=', $code)->first();
|
||||
|
||||
if (!$subscriber) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
return View::make('subscribe.manage')
|
||||
->withComponents(Component::all())
|
||||
->withSubscriber($subscriber)
|
||||
->withSubscriptions($subscriber->subscriptions->pluck('component_id')->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the subscription manager for a subscriber.
|
||||
*
|
||||
* @param string|null $code
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function postManage($code = null)
|
||||
{
|
||||
if ($code === null) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$subscriber = Subscriber::where('verify_code', '=', $code)->first();
|
||||
|
||||
if (!$subscriber) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
try {
|
||||
dispatch(new UpdateSubscriberSubscriptionCommand($subscriber, Binput::get('subscriptions')));
|
||||
} catch (ValidationException $e) {
|
||||
dd($e->getMessageBag());
|
||||
|
||||
return Redirect::route('subscribe.manage', $subscriber->verify_code)
|
||||
->withInput(Binput::all())
|
||||
->withTitle(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
|
||||
->withErrors($e->getMessageBag());
|
||||
}
|
||||
|
||||
return Redirect::route('subscribe.manage', $subscriber->verify_code)
|
||||
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.subscribed')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ class Kernel extends HttpKernel
|
||||
protected $middleware = [
|
||||
'Fideloper\Proxy\TrustProxies',
|
||||
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -40,6 +39,7 @@ class Kernel extends HttpKernel
|
||||
'Illuminate\Foundation\Http\Middleware\VerifyCsrfToken',
|
||||
],
|
||||
'api' => [
|
||||
'Barryvdh\Cors\HandleCors',
|
||||
'CachetHQ\Cachet\Http\Middleware\Acceptable',
|
||||
'CachetHQ\Cachet\Http\Middleware\Timezone',
|
||||
],
|
||||
@@ -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',
|
||||
'throttling' => 'GrahamCampbell\Throttle\Http\Middleware\ThrottleMiddleware',
|
||||
'throttle' => 'Illuminate\Routing\Middleware\ThrottleRequests',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class Acceptable
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, $type = null)
|
||||
{
|
||||
if (!$request->accepts($type ?: 'accept:application/json')) {
|
||||
if (!$request->accepts($type ?: 'application/json')) {
|
||||
throw new NotAcceptableHttpException();
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ class Localize
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
// Early exit optimization.
|
||||
if (!$this->config->get('setting.automatic_localization')) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$supportedLanguages = $request->getLanguages();
|
||||
$userLanguage = $this->config->get('app.locale');
|
||||
|
||||
|
||||
@@ -29,9 +29,10 @@ class ApiRoutes
|
||||
*/
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group(['namespace' => 'Api', 'prefix' => 'api/v1', 'middleware' => ['api']], function ($router) {
|
||||
$router->group(['middleware' => ['auth.api']], function ($router) {
|
||||
$router->group(['namespace' => 'Api', 'prefix' => 'api/v1', 'middleware' => ['api']], function (Registrar $router) {
|
||||
$router->group(['middleware' => ['auth.api']], function (Registrar $router) {
|
||||
$router->get('ping', 'GeneralController@ping');
|
||||
$router->get('version', 'GeneralController@version');
|
||||
|
||||
$router->get('components', 'ComponentController@getComponents');
|
||||
$router->get('components/groups', 'ComponentGroupController@getGroups');
|
||||
@@ -46,7 +47,7 @@ class ApiRoutes
|
||||
$router->get('metrics/{metric}/points', 'MetricController@getMetricPoints');
|
||||
});
|
||||
|
||||
$router->group(['middleware' => ['auth.api:true']], function ($router) {
|
||||
$router->group(['middleware' => ['auth.api:true']], function (Registrar $router) {
|
||||
$router->get('subscribers', 'SubscriberController@getSubscribers');
|
||||
|
||||
$router->post('components', 'ComponentController@postComponents');
|
||||
|
||||
@@ -29,7 +29,7 @@ class AuthRoutes
|
||||
*/
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group(['as' => 'auth.', 'middleware' => ['web', 'ready'], 'prefix' => 'auth'], function ($router) {
|
||||
$router->group(['as' => 'auth.', 'middleware' => ['web', 'ready'], 'prefix' => 'auth'], function (Registrar $router) {
|
||||
$router->get('login', [
|
||||
'middleware' => 'guest',
|
||||
'as' => 'login',
|
||||
@@ -37,7 +37,7 @@ class AuthRoutes
|
||||
]);
|
||||
|
||||
$router->post('login', [
|
||||
'middleware' => ['guest', 'throttling:10,10'],
|
||||
'middleware' => ['guest', 'throttle:10,10'],
|
||||
'uses' => 'AuthController@postLogin',
|
||||
]);
|
||||
|
||||
@@ -47,7 +47,7 @@ class AuthRoutes
|
||||
]);
|
||||
|
||||
$router->post('2fa', [
|
||||
'middleware' => ['throttling:10,10'],
|
||||
'middleware' => ['throttle:10,10'],
|
||||
'uses' => 'AuthController@postTwoFactor',
|
||||
]);
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ class DashboardRoutes
|
||||
*/
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group(['middleware' => ['web', 'auth'], 'prefix' => 'dashboard', 'namespace' => 'Dashboard', 'as' => 'dashboard.'], function ($router) {
|
||||
$router->group(['middleware' => ['web', 'auth'], 'prefix' => 'dashboard', 'namespace' => 'Dashboard', 'as' => 'dashboard.'], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'index',
|
||||
'uses' => 'DashboardController@showDashboard',
|
||||
]);
|
||||
|
||||
$router->group(['as' => 'components.', 'prefix' => 'components'], function ($router) {
|
||||
$router->group(['as' => 'components.', 'prefix' => 'components'], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'index',
|
||||
'uses' => 'ComponentController@showComponents',
|
||||
@@ -68,7 +68,7 @@ class DashboardRoutes
|
||||
$router->post('{component}/edit', 'ComponentController@updateComponentAction');
|
||||
});
|
||||
|
||||
$router->group(['as' => 'incidents.', 'prefix' => 'incidents'], function ($router) {
|
||||
$router->group(['as' => 'incidents.', 'prefix' => 'incidents'], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'index',
|
||||
'uses' => 'IncidentController@showIncidents',
|
||||
@@ -89,7 +89,7 @@ class DashboardRoutes
|
||||
$router->post('{incident}/edit', 'IncidentController@editIncidentAction');
|
||||
});
|
||||
|
||||
$router->group(['as' => 'schedule.', 'prefix' => 'schedule'], function ($router) {
|
||||
$router->group(['as' => 'schedule.', 'prefix' => 'schedule'], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'index',
|
||||
'uses' => 'ScheduleController@showIndex',
|
||||
@@ -110,7 +110,7 @@ class DashboardRoutes
|
||||
]);
|
||||
});
|
||||
|
||||
$router->group(['as' => 'templates.', 'prefix' => 'templates'], function ($router) {
|
||||
$router->group(['as' => 'templates.', 'prefix' => 'templates'], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'index',
|
||||
'uses' => 'IncidentController@showTemplates',
|
||||
@@ -128,7 +128,7 @@ class DashboardRoutes
|
||||
$router->delete('{incident_template}/delete', 'IncidentController@deleteTemplateAction');
|
||||
});
|
||||
|
||||
$router->group(['as' => 'subscribers.', 'prefix' => 'subscribers'], function ($router) {
|
||||
$router->group(['as' => 'subscribers.', 'prefix' => 'subscribers'], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'index',
|
||||
'uses' => 'SubscriberController@showSubscribers',
|
||||
@@ -141,7 +141,7 @@ class DashboardRoutes
|
||||
$router->delete('{subscriber}/delete', 'SubscriberController@deleteSubscriberAction');
|
||||
});
|
||||
|
||||
$router->group(['as' => 'metrics.', 'prefix' => 'metrics'], function ($router) {
|
||||
$router->group(['as' => 'metrics.', 'prefix' => 'metrics'], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'index',
|
||||
'uses' => 'MetricController@showMetrics',
|
||||
@@ -159,13 +159,13 @@ class DashboardRoutes
|
||||
$router->post('{metric}/edit', 'MetricController@editMetricAction');
|
||||
});
|
||||
|
||||
$router->group(['as' => 'team.', 'prefix' => 'team'], function ($router) {
|
||||
$router->group(['as' => 'team.', 'prefix' => 'team'], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'index',
|
||||
'uses' => 'TeamController@showTeamView',
|
||||
]);
|
||||
|
||||
$router->group(['middleware' => 'admin'], function ($router) {
|
||||
$router->group(['middleware' => 'admin'], function (Registrar $router) {
|
||||
$router->get('add', [
|
||||
'as' => 'add',
|
||||
'uses' => 'TeamController@showAddTeamMemberView',
|
||||
@@ -174,7 +174,7 @@ class DashboardRoutes
|
||||
'as' => 'invite',
|
||||
'uses' => 'TeamController@showInviteTeamMemberView',
|
||||
]);
|
||||
$router->get('{user}', 'TeamController@showTeamMemberView');
|
||||
$router->get('{user}', ['as' => 'edit', 'uses' => 'TeamController@showTeamMemberView']);
|
||||
$router->post('add', 'TeamController@postAddUser');
|
||||
$router->post('invite', 'TeamController@postInviteUser');
|
||||
$router->post('{user}', 'TeamController@postUpdateUser');
|
||||
@@ -182,7 +182,7 @@ class DashboardRoutes
|
||||
});
|
||||
});
|
||||
|
||||
$router->group(['as' => 'settings.', 'prefix' => 'settings'], function ($router) {
|
||||
$router->group(['as' => 'settings.', 'prefix' => 'settings'], function (Registrar $router) {
|
||||
$router->get('setup', [
|
||||
'as' => 'setup',
|
||||
'uses' => 'SettingsController@showSetupView',
|
||||
@@ -207,10 +207,14 @@ class DashboardRoutes
|
||||
'as' => 'stylesheet',
|
||||
'uses' => 'SettingsController@showStylesheetView',
|
||||
]);
|
||||
$router->get('customization', [
|
||||
'as' => 'customization',
|
||||
'uses' => 'SettingsController@showCustomizationView',
|
||||
]);
|
||||
$router->post('/', 'SettingsController@postSettings');
|
||||
});
|
||||
|
||||
$router->group(['prefix' => 'user'], function ($router) {
|
||||
$router->group(['prefix' => 'user'], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'user',
|
||||
'uses' => 'UserController@showUser',
|
||||
@@ -219,7 +223,7 @@ class DashboardRoutes
|
||||
$router->get('{user}/api/regen', 'UserController@regenerateApiKey');
|
||||
});
|
||||
|
||||
$router->group(['prefix' => 'api'], function ($router) {
|
||||
$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');
|
||||
|
||||
@@ -29,7 +29,7 @@ class FeedRoutes
|
||||
*/
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group(['middleware' => ['web', 'ready']], function ($router) {
|
||||
$router->group(['middleware' => ['web', 'ready']], function (Registrar $router) {
|
||||
$router->get('/atom/{component_group?}', [
|
||||
'as' => 'feed.atom',
|
||||
'uses' => 'FeedController@atomAction',
|
||||
|
||||
@@ -29,7 +29,7 @@ class SetupRoutes
|
||||
*/
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group(['middleware' => ['web', 'setup']], function ($router) {
|
||||
$router->group(['middleware' => ['web', 'setup']], function (Registrar $router) {
|
||||
$router->controller('setup', 'SetupController');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class SignupRoutes
|
||||
*/
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group(['middleware' => ['web', 'ready', 'guest'], 'as' => 'signup.'], function ($router) {
|
||||
$router->group(['middleware' => ['web', 'ready', 'guest'], 'as' => 'signup.'], function (Registrar $router) {
|
||||
$router->get('signup/invite/{code}', [
|
||||
'as' => 'invite',
|
||||
'uses' => 'SignupController@getSignup',
|
||||
|
||||
@@ -29,7 +29,7 @@ class StatusPageRoutes
|
||||
*/
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group(['middleware' => ['web', 'ready', 'localize']], function ($router) {
|
||||
$router->group(['middleware' => ['web', 'ready', 'localize']], function (Registrar $router) {
|
||||
$router->get('/', [
|
||||
'as' => 'status-page',
|
||||
'uses' => 'StatusPageController@showIndex',
|
||||
@@ -39,6 +39,13 @@ class StatusPageRoutes
|
||||
'as' => 'incident',
|
||||
'uses' => 'StatusPageController@showIncident',
|
||||
]);
|
||||
|
||||
$router->get('metrics/{metric}', [
|
||||
'as' => 'metrics',
|
||||
'uses' => 'StatusPageController@getMetrics',
|
||||
]);
|
||||
|
||||
$router->get('component/{component}/shield', 'StatusPageController@showComponentBadge');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,17 +29,25 @@ class SubscribeRoutes
|
||||
*/
|
||||
public function map(Registrar $router)
|
||||
{
|
||||
$router->group(['middleware' => ['web', 'ready', 'localize'], 'as' => 'subscribe.'], function ($router) {
|
||||
$router->group(['middleware' => ['subscribers']], function ($router) {
|
||||
$router->get('subscribe', [
|
||||
'as' => 'subscribe',
|
||||
'uses' => 'SubscribeController@showSubscribe',
|
||||
]);
|
||||
$router->group(['middleware' => ['web', 'ready', 'localize', 'subscribers'], 'as' => 'subscribe.'], function (Registrar $router) {
|
||||
$router->get('subscribe', [
|
||||
'as' => 'subscribe',
|
||||
'uses' => 'SubscribeController@showSubscribe',
|
||||
]);
|
||||
|
||||
$router->post('subscribe', [
|
||||
'uses' => 'SubscribeController@postSubscribe',
|
||||
]);
|
||||
});
|
||||
$router->post('subscribe', [
|
||||
'uses' => 'SubscribeController@postSubscribe',
|
||||
]);
|
||||
|
||||
$router->get('subscribe/manage/{code}', [
|
||||
'as' => 'manage',
|
||||
'uses' => 'SubscribeController@showManage',
|
||||
]);
|
||||
|
||||
$router->post('subscribe/manage/{code}', [
|
||||
'as' => 'manage',
|
||||
'uses' => 'SubscribeController@postManage',
|
||||
]);
|
||||
|
||||
$router->get('subscribe/verify/{code}', [
|
||||
'as' => 'verify',
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
namespace CachetHQ\Cachet\Models;
|
||||
|
||||
use AltThree\Validator\ValidatingTrait;
|
||||
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
|
||||
use CachetHQ\Cachet\Models\Traits\SortableTrait;
|
||||
use CachetHQ\Cachet\Presenters\ComponentPresenter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -20,7 +22,7 @@ use McCool\LaravelAutoPresenter\HasPresenter;
|
||||
|
||||
class Component extends Model implements HasPresenter
|
||||
{
|
||||
use SoftDeletes, ValidatingTrait;
|
||||
use SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
|
||||
|
||||
/**
|
||||
* List of attributes that have default values.
|
||||
@@ -76,6 +78,34 @@ class Component extends Model implements HasPresenter
|
||||
'link' => 'url',
|
||||
];
|
||||
|
||||
/**
|
||||
* The searchable fields.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $searchable = [
|
||||
'id',
|
||||
'name',
|
||||
'status',
|
||||
'order',
|
||||
'group_id',
|
||||
'enabled',
|
||||
];
|
||||
|
||||
/**
|
||||
* The sortable fields.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $sortable = [
|
||||
'id',
|
||||
'name',
|
||||
'status',
|
||||
'order',
|
||||
'group_id',
|
||||
'enabled',
|
||||
];
|
||||
|
||||
/**
|
||||
* Components can belong to a group.
|
||||
*
|
||||
|
||||
@@ -12,13 +12,25 @@
|
||||
namespace CachetHQ\Cachet\Models;
|
||||
|
||||
use AltThree\Validator\ValidatingTrait;
|
||||
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
|
||||
use CachetHQ\Cachet\Models\Traits\SortableTrait;
|
||||
use CachetHQ\Cachet\Presenters\ComponentGroupPresenter;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use McCool\LaravelAutoPresenter\HasPresenter;
|
||||
|
||||
class ComponentGroup extends Model implements HasPresenter
|
||||
{
|
||||
use ValidatingTrait;
|
||||
use SearchableTrait, SortableTrait, ValidatingTrait;
|
||||
|
||||
/**
|
||||
* The model's attributes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $attributes = [
|
||||
'order' => 0,
|
||||
'collapsed' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be casted to native types.
|
||||
@@ -28,7 +40,7 @@ class ComponentGroup extends Model implements HasPresenter
|
||||
protected $casts = [
|
||||
'name' => 'string',
|
||||
'order' => 'int',
|
||||
'collapsed' => 'bool',
|
||||
'collapsed' => 'int',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -46,7 +58,31 @@ class ComponentGroup extends Model implements HasPresenter
|
||||
public $rules = [
|
||||
'name' => 'required|string',
|
||||
'order' => 'int',
|
||||
'collapsed' => 'bool',
|
||||
'collapsed' => 'int',
|
||||
];
|
||||
|
||||
/**
|
||||
* The searchable fields.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $searchable = [
|
||||
'id',
|
||||
'name',
|
||||
'order',
|
||||
'collapsed',
|
||||
];
|
||||
|
||||
/**
|
||||
* The sortable fields.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $sortable = [
|
||||
'id',
|
||||
'name',
|
||||
'order',
|
||||
'collapsed',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -63,7 +99,17 @@ class ComponentGroup extends Model implements HasPresenter
|
||||
*/
|
||||
public function components()
|
||||
{
|
||||
return $this->hasMany(Component::class, 'group_id', 'id');
|
||||
return $this->hasMany(Component::class, 'group_id', 'id')->orderBy('order');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the incidents relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
|
||||
*/
|
||||
public function incidents()
|
||||
{
|
||||
return $this->hasManyThrough(Incident::class, Component::class, 'id', 'component_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
namespace CachetHQ\Cachet\Models;
|
||||
|
||||
use AltThree\Validator\ValidatingTrait;
|
||||
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
|
||||
use CachetHQ\Cachet\Models\Traits\SortableTrait;
|
||||
use CachetHQ\Cachet\Presenters\IncidentPresenter;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -20,7 +22,7 @@ use McCool\LaravelAutoPresenter\HasPresenter;
|
||||
|
||||
class Incident extends Model implements HasPresenter
|
||||
{
|
||||
use SoftDeletes, ValidatingTrait;
|
||||
use SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
|
||||
|
||||
/**
|
||||
* The attributes that should be casted to native types.
|
||||
@@ -62,6 +64,31 @@ class Incident extends Model implements HasPresenter
|
||||
'message' => 'required',
|
||||
];
|
||||
|
||||
/**
|
||||
* The searchable fields.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $searchable = [
|
||||
'id',
|
||||
'name',
|
||||
'status',
|
||||
'visible',
|
||||
];
|
||||
|
||||
/**
|
||||
* The sortable fields.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $sortable = [
|
||||
'id',
|
||||
'name',
|
||||
'status',
|
||||
'visible',
|
||||
'message',
|
||||
];
|
||||
|
||||
/**
|
||||
* Finds all visible incidents.
|
||||
*
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
namespace CachetHQ\Cachet\Models;
|
||||
|
||||
use AltThree\Validator\ValidatingTrait;
|
||||
use CachetHQ\Cachet\Models\Traits\SortableTrait;
|
||||
use CachetHQ\Cachet\Presenters\MetricPresenter;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use McCool\LaravelAutoPresenter\HasPresenter;
|
||||
|
||||
class Metric extends Model implements HasPresenter
|
||||
{
|
||||
use ValidatingTrait;
|
||||
use SortableTrait, ValidatingTrait;
|
||||
|
||||
/**
|
||||
* The calculation type of sum.
|
||||
@@ -46,6 +47,7 @@ class Metric extends Model implements HasPresenter
|
||||
'calc_type' => 0,
|
||||
'places' => 2,
|
||||
'default_view' => 1,
|
||||
'threshold' => 5,
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -60,6 +62,7 @@ class Metric extends Model implements HasPresenter
|
||||
'calc_type' => 'int',
|
||||
'places' => 'int',
|
||||
'default_view' => 'int',
|
||||
'threshold' => 'int',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -76,6 +79,7 @@ class Metric extends Model implements HasPresenter
|
||||
'calc_type',
|
||||
'places',
|
||||
'default_view',
|
||||
'threshold',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -90,6 +94,20 @@ 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',
|
||||
];
|
||||
|
||||
/**
|
||||
* The sortable fields.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $sortable = [
|
||||
'id',
|
||||
'name',
|
||||
'display_chart',
|
||||
'default_value',
|
||||
'calc_type',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -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',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -48,6 +49,13 @@ class Subscriber extends Model implements HasPresenter
|
||||
'email' => 'required|email',
|
||||
];
|
||||
|
||||
/**
|
||||
* The relations to eager load on every query.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $with = ['subscriptions'];
|
||||
|
||||
/**
|
||||
* Overrides the models boot method.
|
||||
*/
|
||||
@@ -84,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');
|
||||
}
|
||||
}
|
||||
|
||||
43
app/Models/Traits/SearchableTrait.php
Normal file
43
app/Models/Traits/SearchableTrait.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?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\Models\Traits;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* This is the searchable trait.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
trait SearchableTrait
|
||||
{
|
||||
/**
|
||||
* Adds a search scope.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param array $search
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeSearch(Builder $query, array $search = [])
|
||||
{
|
||||
if (empty($search)) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
if (!array_intersect(array_keys($search), $this->searchable)) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
return $query->where($search);
|
||||
}
|
||||
}
|
||||
40
app/Models/Traits/SortableTrait.php
Normal file
40
app/Models/Traits/SortableTrait.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Models\Traits;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* This is the sortable trait.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
trait SortableTrait
|
||||
{
|
||||
/**
|
||||
* Adds a sort scope.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param string $column
|
||||
* @param string $direction
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeSort(Builder $query, $column, $direction)
|
||||
{
|
||||
if (!in_array($column, $this->sortable)) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
return $query->orderBy($column, $direction);
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,25 @@ class ComponentGroupPresenter extends BasePresenter implements Arrayable
|
||||
*/
|
||||
public function collapse_class()
|
||||
{
|
||||
return $this->wrappedObject->collapsed ? 'ion-ios-plus-outline' : 'ion-ios-minus-outline';
|
||||
return $this->is_collapsed() ? 'ion-ios-plus-outline' : 'ion-ios-minus-outline';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the group should be collapsed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_collapsed()
|
||||
{
|
||||
if ($this->wrappedObject->collapsed === 0) {
|
||||
return false;
|
||||
} elseif ($this->wrappedObject->collapsed === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->wrappedObject->components->filter(function ($component) {
|
||||
return $component->status > 1;
|
||||
})->count() === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
|
||||
namespace CachetHQ\Cachet\Presenters;
|
||||
|
||||
use CachetHQ\Cachet\Dates\DateFactory;
|
||||
use CachetHQ\Cachet\Presenters\Traits\TimestampsTrait;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use McCool\LaravelAutoPresenter\BasePresenter;
|
||||
|
||||
class ComponentPresenter extends BasePresenter implements Arrayable
|
||||
@@ -44,6 +46,26 @@ class ComponentPresenter extends BasePresenter implements Arrayable
|
||||
return trans('cachet.components.status.'.$this->wrappedObject->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all tag names for the component names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function tags()
|
||||
{
|
||||
return $this->wrappedObject->tags->pluck('name', 'slug');
|
||||
}
|
||||
|
||||
/**
|
||||
* Present formatted date time.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function updated_at_formatted()
|
||||
{
|
||||
return ucfirst(app(DateFactory::class)->make($this->wrappedObject->updated_at)->format(Config::get('setting.incident_date_format', 'l jS F Y H:i:s')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the presenter instance to an array.
|
||||
*
|
||||
@@ -55,6 +77,7 @@ class ComponentPresenter extends BasePresenter implements Arrayable
|
||||
'created_at' => $this->created_at(),
|
||||
'updated_at' => $this->updated_at(),
|
||||
'status_name' => $this->human_status(),
|
||||
'tags' => $this->tags(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,16 @@ class MetricPresenter extends BasePresenter implements Arrayable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the metric view filter name, used in the API.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function default_view_name()
|
||||
{
|
||||
return trans('cachet.metrics.filter.'.$this->trans_string_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the metric translation view filter name.
|
||||
*
|
||||
@@ -57,8 +67,9 @@ class MetricPresenter 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(),
|
||||
'default_view_name' => $this->default_view_name(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace CachetHQ\Cachet\Presenters;
|
||||
use CachetHQ\Cachet\Dates\DateFactory;
|
||||
use CachetHQ\Cachet\Presenters\Traits\TimestampsTrait;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use McCool\LaravelAutoPresenter\BasePresenter;
|
||||
|
||||
class SubscriberPresenter extends BasePresenter implements Arrayable
|
||||
@@ -27,7 +28,7 @@ class SubscriberPresenter extends BasePresenter implements Arrayable
|
||||
*/
|
||||
public function verified_at()
|
||||
{
|
||||
return app(DateFactory::class)->make($this->wrappedObject->verified_at)->toDateTimeString();
|
||||
return ucfirst(app(DateFactory::class)->make($this->wrappedObject->verified_at)->format(Config::get('setting.incident_date_format', 'l jS F Y H:i:s')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -32,14 +32,21 @@ class MySqlRepository implements MetricInterface
|
||||
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'))->sub(new DateInterval('PT'.$minute.'M'));
|
||||
$timeInterval = $dateTime->format('YmdHi');
|
||||
|
||||
$points = $metric->points()
|
||||
->whereRaw('DATE_FORMAT(created_at, "%Y%m%d%H%i") = '.$timeInterval)
|
||||
->groupBy(DB::raw('HOUR(created_at), MINUTE(created_at)'));
|
||||
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$value = $points->sum('value');
|
||||
$queryType = 'SUM(mp.`value` * mp.`counter`) AS `value`';
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = 'AVG(mp.`value` * mp.`counter`) AS `value`';
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
|
||||
$points = DB::select("SELECT {$queryType} FROM metrics m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND DATE_FORMAT(mp.`created_at`, '%Y%m%d%H%i') = :timeInterval GROUP BY HOUR(mp.`created_at`), MINUTE(mp.`created_at`)", [
|
||||
'metricId' => $metric->id,
|
||||
'timeInterval' => $timeInterval,
|
||||
]);
|
||||
|
||||
if (isset($points[0]) && !($value = $points[0]->value)) {
|
||||
$value = 0;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
@@ -62,14 +69,21 @@ class MySqlRepository implements MetricInterface
|
||||
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
|
||||
$hourInterval = $dateTime->format('YmdH');
|
||||
|
||||
$points = $metric->points()
|
||||
->whereRaw('DATE_FORMAT(created_at, "%Y%m%d%H") = '.$hourInterval)
|
||||
->groupBy(DB::raw('HOUR(created_at)'));
|
||||
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$value = $points->sum('value');
|
||||
$queryType = 'SUM(mp.`value` * mp.`counter`) AS `value`';
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = 'AVG(mp.`value` * mp.`counter`) AS `value`';
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
|
||||
$points = DB::select("SELECT {$queryType} FROM metrics m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND DATE_FORMAT(mp.`created_at`, '%Y%m%d%H') = :hourInterval GROUP BY HOUR(mp.`created_at`)", [
|
||||
'metricId' => $metric->id,
|
||||
'hourInterval' => $hourInterval,
|
||||
]);
|
||||
|
||||
if (isset($points[0]) && !($value = $points[0]->value)) {
|
||||
$value = 0;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
@@ -90,15 +104,21 @@ class MySqlRepository implements MetricInterface
|
||||
{
|
||||
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
|
||||
|
||||
$points = $metric->points()
|
||||
->whereRaw('created_at BETWEEN DATE_SUB(created_at, INTERVAL 1 WEEK) AND NOW()')
|
||||
->whereRaw('DATE_FORMAT(created_at, "%Y%m%d") = '.$dateTime->format('Ymd'))
|
||||
->groupBy(DB::raw('DATE_FORMAT(created_at, "%Y%m%d")'));
|
||||
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$value = $points->sum('value');
|
||||
$queryType = 'SUM(mp.`value` * mp.`counter`) AS `value`';
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = 'AVG(mp.`value` * mp.`counter`) AS `value`';
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
|
||||
$points = DB::select("SELECT {$queryType} FROM metrics m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND mp.`created_at` BETWEEN DATE_SUB(mp.`created_at`, INTERVAL 1 WEEK) AND DATE_ADD(NOW(), INTERVAL 1 DAY) AND DATE_FORMAT(mp.`created_at`, '%Y%m%d') = :timeInterval GROUP BY DATE_FORMAT(mp.`created_at`, '%Y%m%d')", [
|
||||
'metricId' => $metric->id,
|
||||
'timeInterval' => $dateTime->format('Ymd'),
|
||||
]);
|
||||
|
||||
if (isset($points[0]) && !($value = $points[0]->value)) {
|
||||
$value = 0;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
|
||||
@@ -30,26 +30,24 @@ class PgSqlRepository implements MetricInterface
|
||||
public function getPointsLastHour(Metric $metric, $hour, $minute)
|
||||
{
|
||||
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'))->sub(new DateInterval('PT'.$minute.'M'));
|
||||
$hourInterval = $dateTime->format('YmdHi');
|
||||
|
||||
// Default metrics calculations.
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$queryType = 'sum(metric_points.value)';
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$queryType = 'avg(metric_points.value)';
|
||||
$queryType = 'avg(metric_points.value * metric_points.counter)';
|
||||
} else {
|
||||
$queryType = 'sum(metric_points.value)';
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
}
|
||||
|
||||
$query = DB::select("select {$queryType} as aggregate FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metric_id AND to_char(metric_points.created_at, 'YYYYMMDDHH24MI') = :timestamp GROUP BY to_char(metric_points.created_at, 'HHMI')", [
|
||||
'metric_id' => $metric->id,
|
||||
'timestamp' => $hourInterval,
|
||||
$value = 0;
|
||||
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metricId AND to_char(metric_points.created_at, 'YYYYMMDDHH24MI') = :timeInterval GROUP BY to_char(metric_points.created_at, 'HHMI')", [
|
||||
'metricId' => $metric->id,
|
||||
'timeInterval' => $dateTime->format('YmdHi'),
|
||||
]);
|
||||
|
||||
if (isset($query[0])) {
|
||||
$value = $query[0]->aggregate;
|
||||
} else {
|
||||
$value = 0;
|
||||
$value = $query[0]->value;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
@@ -70,26 +68,24 @@ class PgSqlRepository implements MetricInterface
|
||||
public function getPointsByHour(Metric $metric, $hour)
|
||||
{
|
||||
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
|
||||
$hourInterval = $dateTime->format('YmdH');
|
||||
|
||||
// Default metrics calculations.
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$queryType = 'sum(metric_points.value)';
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$queryType = 'avg(metric_points.value)';
|
||||
$queryType = 'avg(metric_points.value * metric_points.counter)';
|
||||
} else {
|
||||
$queryType = 'sum(metric_points.value)';
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
}
|
||||
|
||||
$query = DB::select("select {$queryType} as aggregate FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metric_points.metric_id = :metric_id AND to_char(metric_points.created_at, 'YYYYMMDDHH24') = :timestamp GROUP BY to_char(metric_points.created_at, 'H')", [
|
||||
'metric_id' => $metric->id,
|
||||
'timestamp' => $hourInterval,
|
||||
$value = 0;
|
||||
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metric_points.metric_id = :metricId AND to_char(metric_points.created_at, 'YYYYMMDDHH24') = :timeInterval GROUP BY to_char(metric_points.created_at, 'H')", [
|
||||
'metricId' => $metric->id,
|
||||
'timeInterval' => $dateTime->format('YmdH'),
|
||||
]);
|
||||
|
||||
if (isset($query[0])) {
|
||||
$value = $query[0]->aggregate;
|
||||
} else {
|
||||
$value = 0;
|
||||
$value = $query[0]->value;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
@@ -110,15 +106,20 @@ class PgSqlRepository implements MetricInterface
|
||||
{
|
||||
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
|
||||
|
||||
$points = $metric->points()
|
||||
->whereRaw('created_at BETWEEN (created_at - interval \'1 week\') AND now()')
|
||||
->whereRaw('to_char(created_at, \'YYYYMMDD\') = \''.$dateTime->format('Ymd').'\'')
|
||||
->groupBy(DB::raw('to_char(created_at, \'YYYYMMDD\')'));
|
||||
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$value = $points->sum('value');
|
||||
$queryType = 'sum(mp.value * mp.counter) AS value';
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = 'avg(mp.value * mp.counter) AS value';
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
$points = DB::select("SELECT {$queryType} FROM metrics m INNER JOIN metric_points mp ON m.id = mp.metric_id WHERE m.id = :metricId AND mp.created_at BETWEEN (mp.created_at - interval '1 week') AND (now() + interval '1 day') AND to_char(mp.created_at, 'YYYYMMDD') = :timeInterval GROUP BY to_char(mp.created_at, 'YYYYMMDD')", [
|
||||
'metricId' => $metric->id,
|
||||
'timeInterval' => $dateTime->format('Ymd'),
|
||||
]);
|
||||
|
||||
if (isset($points[0]) && !($value = $points[0]->value)) {
|
||||
$value = 0;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
|
||||
@@ -30,16 +30,24 @@ class SqliteRepository implements MetricInterface
|
||||
public function getPointsLastHour(Metric $metric, $hour, $minute)
|
||||
{
|
||||
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'))->sub(new DateInterval('PT'.$minute.'M'));
|
||||
$hourInterval = $dateTime->format('YmdHi');
|
||||
|
||||
$points = $metric->points()
|
||||
->whereRaw('strftime("%Y%m%d%H%M", created_at) = "'.$hourInterval.'"')
|
||||
->groupBy(DB::raw('strftime("%H%M", created_at)'));
|
||||
|
||||
// Default metrics calculations.
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$value = $points->sum('value');
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = 'avg(metric_points.value * metric_points.counter)';
|
||||
} else {
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metricId AND strftime('%Y%m%d%H%M', metric_points.created_at) = :timeInterval GROUP BY strftime('%H%M', metric_points.created_at)", [
|
||||
'metricId' => $metric->id,
|
||||
'timeInterval' => $dateTime->format('YmdHi'),
|
||||
]);
|
||||
|
||||
if (isset($query[0])) {
|
||||
$value = $query[0]->value;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
@@ -60,16 +68,24 @@ class SqliteRepository implements MetricInterface
|
||||
public function getPointsByHour(Metric $metric, $hour)
|
||||
{
|
||||
$dateTime = (new Date())->sub(new DateInterval('PT'.$hour.'H'));
|
||||
$hourInterval = $dateTime->format('YmdH');
|
||||
|
||||
$points = $metric->points()
|
||||
->whereRaw('strftime("%Y%m%d%H", created_at) = "'.$hourInterval.'"')
|
||||
->groupBy(DB::raw('strftime("%H", created_at)'));
|
||||
|
||||
// Default metrics calculations.
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$value = $points->sum('value');
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = 'avg(metric_points.value * metric_points.counter)';
|
||||
} else {
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metricId AND strftime('%Y%m%d%H', metric_points.created_at) = :timeInterval GROUP BY strftime('%H', metric_points.created_at)", [
|
||||
'metricId' => $metric->id,
|
||||
'timeInterval' => $dateTime->format('YmdH'),
|
||||
]);
|
||||
|
||||
if (isset($query[0])) {
|
||||
$value = $query[0]->value;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
@@ -90,15 +106,23 @@ class SqliteRepository implements MetricInterface
|
||||
{
|
||||
$dateTime = (new Date())->sub(new DateInterval('P'.$day.'D'));
|
||||
|
||||
$points = $metric->points()
|
||||
->whereRaw('created_at > date("now", "-7 day")')
|
||||
->whereRaw('strftime("%Y%m%d", created_at) = "'.$dateTime->format('Ymd').'"')
|
||||
->groupBy(DB::raw('strftime("%Y%m%d", created_at)'));
|
||||
|
||||
// Default metrics calculations.
|
||||
if (!isset($metric->calc_type) || $metric->calc_type == Metric::CALC_SUM) {
|
||||
$value = $points->sum('value');
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
} elseif ($metric->calc_type == Metric::CALC_AVG) {
|
||||
$value = $points->avg('value');
|
||||
$queryType = 'avg(metric_points.value * metric_points.counter)';
|
||||
} else {
|
||||
$queryType = 'sum(metric_points.value * metric_points.counter)';
|
||||
}
|
||||
|
||||
$value = 0;
|
||||
$query = DB::select("select {$queryType} as value FROM metrics JOIN metric_points ON metric_points.metric_id = metrics.id WHERE metrics.id = :metricId AND metric_points.created_at > date('now', '-7 day') AND strftime('%Y%m%d', metric_points.created_at) = :timeInterval GROUP BY strftime('%Y%m%d', metric_points.created_at)", [
|
||||
'metricId' => $metric->id,
|
||||
'timeInterval' => $dateTime->format('Ymd'),
|
||||
]);
|
||||
|
||||
if (isset($query[0])) {
|
||||
$value = $query[0]->value;
|
||||
}
|
||||
|
||||
if ($value === 0 && $metric->default_value != $value) {
|
||||
|
||||
103
app/Settings/Cache.php
Normal file
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
|
||||
*
|
||||
@@ -70,6 +76,20 @@ class Repository
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a setting.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete($name)
|
||||
{
|
||||
$this->stale = true;
|
||||
|
||||
$this->model->where('name', $name)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the config state stale?
|
||||
*
|
||||
103
app/Subscribers/CommandSubscriber.php
Normal file
103
app/Subscribers/CommandSubscriber.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\Subscribers;
|
||||
|
||||
use CachetHQ\Cachet\Settings\Cache;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Config\Repository;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
/**
|
||||
* This is the command subscriber class.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
* @author Graham Campbell <graham@alt-three.com>
|
||||
*/
|
||||
class CommandSubscriber
|
||||
{
|
||||
/**
|
||||
* The settings cache instance.
|
||||
*
|
||||
* @var \CachetHQ\Cachet\Settings\Cache
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The config repository instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Create a new command subscriber instance.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Settings\Cache $cache
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Cache $cache, Repository $config)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $events
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen('command.installing', __CLASS__.'@fire', 5);
|
||||
$events->listen('command.updating', __CLASS__.'@fire', 5);
|
||||
$events->listen('command.resetting', __CLASS__.'@fire', 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the settings cache, and backup the databases.
|
||||
*
|
||||
* @param \Illuminate\Console\Command $command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function fire(Command $command)
|
||||
{
|
||||
$command->line('Clearing settings cache...');
|
||||
|
||||
$this->cache->clear();
|
||||
|
||||
$command->line('Settings cache cleared!');
|
||||
|
||||
$command->line('Backing up database...');
|
||||
|
||||
try {
|
||||
$command->call('db:backup', [
|
||||
'--compression' => 'gzip',
|
||||
'--database' => $this->config->get('database.default'),
|
||||
'--destination' => 'local',
|
||||
'--destinationPath' => Carbon::now()->format('Y-m-d H.i.s'),
|
||||
'--no-interaction' => true,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$command->error($e->getMessage());
|
||||
$command->line('Backup skipped!');
|
||||
}
|
||||
|
||||
$command->line('Backup completed!');
|
||||
}
|
||||
}
|
||||
2
bootstrap/cachet/.gitignore
vendored
Normal file
2
bootstrap/cachet/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
22
bower.json
22
bower.json
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "Cachet",
|
||||
"dependencies": {
|
||||
"animate-sass": "~0.6",
|
||||
"autosize": "~3.0.15",
|
||||
"bootstrap-sass": "~3.3",
|
||||
"chartjs": "~1.0",
|
||||
"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.1",
|
||||
"messenger": "~1.4",
|
||||
"moment": "~2.10",
|
||||
"Sortable": "~1.4",
|
||||
"sweetalert": "~1.1"
|
||||
}
|
||||
}
|
||||
@@ -20,32 +20,32 @@
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"laravel/framework": "~5.2.20",
|
||||
"alt-three/bus": "^1.0",
|
||||
"alt-three/emoji": "^3.0",
|
||||
"alt-three/validator": "^1.4",
|
||||
"asm89/stack-cors": "dev-master as 0.2.2",
|
||||
"barryvdh/laravel-cors": "^0.7.3",
|
||||
"laravel/framework": "5.2.32",
|
||||
"alt-three/badger": "^3.1",
|
||||
"alt-three/bus": "^1.1",
|
||||
"alt-three/emoji": "^3.1",
|
||||
"alt-three/validator": "^1.5",
|
||||
"backup-manager/laravel": "^1.1",
|
||||
"barryvdh/laravel-cors": "^0.8",
|
||||
"doctrine/dbal": "^2.5",
|
||||
"fideloper/proxy": "^3.1",
|
||||
"graham-campbell/binput": "^3.3",
|
||||
"graham-campbell/core": "^4.2",
|
||||
"graham-campbell/markdown": "^6.0",
|
||||
"graham-campbell/throttle": "^5.1",
|
||||
"graham-campbell/exceptions": "^8.3",
|
||||
"guzzlehttp/guzzle": "^6.1",
|
||||
"jenssegers/date": "^3.1",
|
||||
"mccool/laravel-auto-presenter": "^4.2",
|
||||
"graham-campbell/binput": "^3.4",
|
||||
"graham-campbell/core": "^5.1",
|
||||
"graham-campbell/exceptions": "^8.5",
|
||||
"graham-campbell/markdown": "^6.1",
|
||||
"guzzlehttp/guzzle": "^6.2",
|
||||
"jenssegers/date": "^3.2",
|
||||
"mccool/laravel-auto-presenter": "^4.3",
|
||||
"pragmarx/google2fa": "^0.7.1",
|
||||
"rcrowe/twigbridge": "^0.9.1",
|
||||
"rcrowe/twigbridge": "^0.9.2",
|
||||
"roumen/feed": "^2.10"
|
||||
},
|
||||
"require-dev": {
|
||||
"alt-three/testbench": "^1.1",
|
||||
"filp/whoops": "^2.0",
|
||||
"fzaninotto/faker": "^1.5",
|
||||
"alt-three/testbench": "^1.4",
|
||||
"filp/whoops": "^2.1",
|
||||
"fzaninotto/faker": "^1.6",
|
||||
"graham-campbell/testbench-core": "^1.1",
|
||||
"mockery/mockery": "0.9.4",
|
||||
"mockery/mockery": "0.9.5",
|
||||
"phpunit/phpunit": "4.8.21",
|
||||
"symfony/css-selector": "^3.0",
|
||||
"symfony/dom-crawler": "^3.0"
|
||||
@@ -75,20 +75,16 @@
|
||||
"php artisan key:generate"
|
||||
],
|
||||
"post-install-cmd": [
|
||||
"php artisan clear-compiled",
|
||||
"Illuminate\\Foundation\\ComposerScripts::postInstall",
|
||||
"php artisan optimize --force",
|
||||
"php artisan config:cache",
|
||||
"php artisan route:cache",
|
||||
"chmod -R 755 storage"
|
||||
],
|
||||
"pre-update-cmd": [
|
||||
"php artisan clear-compiled"
|
||||
"php artisan route:cache"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
|
||||
"php artisan optimize --force",
|
||||
"php artisan config:cache",
|
||||
"php artisan route:cache",
|
||||
"chmod -R 755 storage"
|
||||
"php artisan route:cache"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
@@ -99,7 +95,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1-dev"
|
||||
"dev-master": "2.3-dev"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
|
||||
903
composer.lock
generated
903
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -160,7 +160,9 @@ return [
|
||||
/*
|
||||
* Packages Service Providers...
|
||||
*/
|
||||
'AltThree\Badger\BadgerServiceProvider',
|
||||
'AltThree\Emoji\EmojiServiceProvider',
|
||||
'BackupManager\Laravel\Laravel5ServiceProvider',
|
||||
'Barryvdh\Cors\ServiceProvider',
|
||||
'Fideloper\Proxy\TrustedProxyServiceProvider',
|
||||
'GrahamCampbell\Binput\BinputServiceProvider',
|
||||
@@ -168,7 +170,6 @@ return [
|
||||
'GrahamCampbell\Core\CoreServiceProvider',
|
||||
'GrahamCampbell\Markdown\MarkdownServiceProvider',
|
||||
'GrahamCampbell\Security\SecurityServiceProvider',
|
||||
'GrahamCampbell\Throttle\ThrottleServiceProvider',
|
||||
'Jenssegers\Date\DateServiceProvider',
|
||||
'McCool\LaravelAutoPresenter\AutoPresenterServiceProvider',
|
||||
'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider',
|
||||
@@ -180,9 +181,9 @@ return [
|
||||
*/
|
||||
'CachetHQ\Cachet\Foundation\Providers\AppServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\ComposerServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\ConsoleServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\ConfigServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\EventServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\GitHubServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\RepositoryServiceProvider',
|
||||
'CachetHQ\Cachet\Foundation\Providers\RouteServiceProvider',
|
||||
|
||||
|
||||
70
config/backup-manager.php
Normal file
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' => '',
|
||||
],
|
||||
];
|
||||
@@ -53,14 +53,16 @@ return [
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path().'/framework/cache',
|
||||
'path' => storage_path('framework/cache'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'servers' => [
|
||||
[
|
||||
'host' => '127.0.0.1', 'port' => 11211, 'weight' => 100,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 11211,
|
||||
'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -10,37 +10,20 @@
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Laravel CORS Defaults
|
||||
| Laravel CORS
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The defaults are the default values applied to all the paths that match,
|
||||
| unless overridden in a specific URL configuration.
|
||||
| If you want them to apply to everything, you must define a path with *.
|
||||
|
|
||||
| allowedOrigins, allowedHeaders and allowedMethods can be set to ['*']
|
||||
| to accept any value, the allowed methods however have to be explicitly listed.
|
||||
| allowedOrigins, allowedHeaders and allowedMethods can be set to array('*')
|
||||
| to accept any value.
|
||||
|
|
||||
*/
|
||||
'defaults' => [
|
||||
'supportsCredentials' => true,
|
||||
'allowedOrigins' => ['*'],
|
||||
'allowedHeaders' => [],
|
||||
'allowedMethods' => [],
|
||||
'exposedHeaders' => [],
|
||||
'maxAge' => 0,
|
||||
'hosts' => [],
|
||||
],
|
||||
|
||||
'paths' => [
|
||||
'api/v1/*' => [
|
||||
'allowedOrigins' => [],
|
||||
'allowedHeaders' => ['X-Cachet-Token'],
|
||||
'allowedMethods' => ['*'],
|
||||
'maxAge' => 3600,
|
||||
],
|
||||
],
|
||||
|
||||
'supportsCredentials' => false,
|
||||
'allowedOrigins' => ['*'],
|
||||
'allowedHeaders' => ['X-Cachet-Token'],
|
||||
'allowedMethods' => ['*'],
|
||||
'exposedHeaders' => [],
|
||||
'maxAge' => 3600,
|
||||
'hosts' => [],
|
||||
];
|
||||
|
||||
@@ -57,8 +57,8 @@ return [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => env('DB_HOST', storage_path().'/database.sqlite'),
|
||||
'prefix' => '',
|
||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||
'prefix' => env('DB_PREFIX', null),
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
@@ -67,11 +67,12 @@ return [
|
||||
'database' => env('DB_DATABASE', null),
|
||||
'username' => env('DB_USERNAME', null),
|
||||
'password' => env('DB_PASSWORD', null),
|
||||
'port' => env('DB_PORT', null),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'charset' => 'utf8',
|
||||
'collation' => 'utf8_unicode_ci',
|
||||
'prefix' => '',
|
||||
'prefix' => env('DB_PREFIX', null),
|
||||
'strict' => false,
|
||||
'engine' => null,
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
@@ -80,10 +81,10 @@ return [
|
||||
'database' => env('DB_DATABASE', null),
|
||||
'username' => env('DB_USERNAME', null),
|
||||
'password' => env('DB_PASSWORD', null),
|
||||
'port' => env('DB_PORT', null),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => '',
|
||||
'schema' => 'public',
|
||||
'prefix' => env('DB_PREFIX', null),
|
||||
'schema' => env('DB_SCHEMA', 'public'),
|
||||
],
|
||||
|
||||
'sqlsrv' => [
|
||||
@@ -93,7 +94,7 @@ return [
|
||||
'username' => env('DB_USERNAME', null),
|
||||
'password' => env('DB_PASSWORD', null),
|
||||
'port' => env('DB_PORT', null),
|
||||
'prefix' => '',
|
||||
'prefix' => env('DB_PREFIX', null),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
@@ -54,14 +54,13 @@ return [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path().'/app',
|
||||
'root' => storage_path('app'),
|
||||
],
|
||||
|
||||
'ftp' => [
|
||||
'driver' => 'ftp',
|
||||
'host' => 'ftp.example.com',
|
||||
'username' => 'your-username',
|
||||
'password' => 'your-password',
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'visibility' => 'public',
|
||||
],
|
||||
|
||||
's3' => [
|
||||
@@ -72,16 +71,6 @@ return [
|
||||
'bucket' => 'your-bucket',
|
||||
],
|
||||
|
||||
'rackspace' => [
|
||||
'driver' => 'rackspace',
|
||||
'username' => 'your-username',
|
||||
'key' => 'your-key',
|
||||
'container' => 'your-container',
|
||||
'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/',
|
||||
'region' => 'IAD',
|
||||
'url_type' => 'publicURL',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
170
config/langs.php
170
config/langs.php
@@ -11,38 +11,140 @@
|
||||
|
||||
return [
|
||||
// Enabled langs
|
||||
'af' => 'Afrikaans',
|
||||
'ar' => 'Arabic',
|
||||
'ca' => 'Catalan',
|
||||
'cs' => 'Czech',
|
||||
'da' => 'Danish',
|
||||
'de' => 'Deutsch',
|
||||
'el' => 'Greek',
|
||||
'en' => 'English',
|
||||
'en-UD' => 'CrowdIn - InContext Localization',
|
||||
'es' => 'Español',
|
||||
'fa' => 'Persian',
|
||||
'fi' => 'Finnish',
|
||||
'fr' => 'Français',
|
||||
'he' => 'Hebrew',
|
||||
'hu' => 'Hungarian',
|
||||
'id' => 'Indonesian',
|
||||
'it' => 'Italiano',
|
||||
'ja' => 'Japanese',
|
||||
'ko' => '한글',
|
||||
'nl' => 'Nederlands',
|
||||
'no' => 'Norwegian',
|
||||
'pl' => 'Polski',
|
||||
'pt-BR' => 'Portuguese',
|
||||
'pt-PT' => 'Portuguese, Brazilian',
|
||||
'ro' => 'Romanian',
|
||||
'ru' => 'Русский',
|
||||
'sq' => 'Albanian',
|
||||
'sr' => 'Sebrian (Cyrillic)',
|
||||
'sv-SE' => 'Swedish',
|
||||
'tr' => 'Turkish',
|
||||
'uk' => 'Ukranian',
|
||||
'vi' => 'Vietnamese',
|
||||
'zh-CN' => '简体中文',
|
||||
'zh-TW' => '繁體中文',
|
||||
'af' => [
|
||||
'name' => 'Afrikaans',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'ar' => [
|
||||
'name' => 'Arabic',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'ca' => [
|
||||
'name' => 'Catalan',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'cs' => [
|
||||
'name' => 'Czech',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'da' => [
|
||||
'name' => 'Danish',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'de' => [
|
||||
'name' => 'Deutsch',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'el' => [
|
||||
'name' => 'Greek',
|
||||
'subset' => 'greek,greek-ext',
|
||||
],
|
||||
'en' => [
|
||||
'name' => 'English',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'en-UD' => [
|
||||
'name' => 'CrowdIn - InContext Localization',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'es' => [
|
||||
'name' => 'Español',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'fa' => [
|
||||
'name' => 'Persian',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'fi' => [
|
||||
'name' => 'Finnish',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'fr' => [
|
||||
'name' => 'Français',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'he' => [
|
||||
'name' => 'Hebrew',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'hu' => [
|
||||
'name' => 'Hungarian',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'id' => [
|
||||
'name' => 'Indonesian',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'it' => [
|
||||
'name' => 'Italiano',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'ja' => [
|
||||
'name' => 'Japanese',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'ko' => [
|
||||
'name' => '한글',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'nl' => [
|
||||
'name' => 'Nederlands',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'no' => [
|
||||
'name' => 'Norwegian',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'pl' => [
|
||||
'name' => 'Polski',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'pt-BR' => [
|
||||
'name' => 'Portuguese',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'pt-PT' => [
|
||||
'name' => 'Portuguese, Brazilian',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'ro' => [
|
||||
'name' => 'Romanian',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'ru' => [
|
||||
'name' => 'Русский',
|
||||
'subset' => 'latin,cyrillic',
|
||||
],
|
||||
'sq' => [
|
||||
'name' => 'Albanian',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'sr' => [
|
||||
'name' => 'Sebrian (Cyrillic)',
|
||||
'subset' => 'latin,cyrillic,cyrillic-ext',
|
||||
],
|
||||
'sv-SE' => [
|
||||
'name' => 'Swedish',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'tr' => [
|
||||
'name' => 'Turkish',
|
||||
'subset' => 'latin,latin-ext',
|
||||
],
|
||||
'uk' => [
|
||||
'name' => 'Ukranian',
|
||||
'subset' => 'latin,cyrillic-ext',
|
||||
],
|
||||
'vi' => [
|
||||
'name' => 'Vietnamese',
|
||||
'subset' => 'latin,vietnamese',
|
||||
],
|
||||
'zh-CN' => [
|
||||
'name' => '简体中文',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
'zh-TW' => [
|
||||
'name' => '繁體中文',
|
||||
'subset' => 'latin',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -20,7 +20,8 @@ return [
|
||||
| sending of e-mail. You may specify which one you're using throughout
|
||||
| your application here. By default, Laravel is setup for SMTP mail.
|
||||
|
|
||||
| Supported: "smtp", "mail", "sendmail", "mailgun", "mandrill", "ses", "log"
|
||||
| Supported: "smtp", "mail", "sendmail", "mailgun", "mandrill",
|
||||
| "ses", "sparkpost", "log"
|
||||
|
|
||||
*/
|
||||
|
||||
|
||||
@@ -42,10 +42,8 @@ return [
|
||||
'region' => 'us-east-1',
|
||||
],
|
||||
|
||||
'stripe' => [
|
||||
'model' => 'CachetHQ\Cachet\Models\User',
|
||||
'key' => env('STRIPE_KEY'),
|
||||
'secret' => env('STRIPE_SECRET'),
|
||||
'sparkpost' => [
|
||||
'secret' => env('SPARKPOST_SECRET'),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user