Compare commits

...

49 Commits

Author SHA1 Message Date
James Brooks
d5d92f5a3d Update original incident with new update status 2016-08-04 20:28:06 +01:00
Graham Campbell
247c1565a4 Merge branch 'master' into develop 2016-07-29 13:26:02 -04:00
Graham Campbell
8f5afff4a2 Merge branch '2.4' 2016-07-29 13:14:33 -04:00
Graham Campbell
a019d82010 Merge branch '2.4' 2016-07-25 23:03:58 +01:00
Graham Campbell
5b5db06ce3 Updated composer.lock hashes 2016-07-18 19:44:19 +01:00
Graham Campbell
b6635d30ee Merge branch '2.4' 2016-07-18 19:39:25 +01:00
Graham Campbell
5ae23b64d2 Bumped version 2016-07-18 19:39:18 +01:00
Graham Campbell
7583fedaec Updated laravel 2016-07-14 11:25:50 -07:00
Graham Campbell
eecd78496c Merge branch 'master' into develop 2016-07-14 11:25:16 -07:00
Graham Campbell
48dc39122f Merge branch 'master' into develop 2016-07-13 19:07:01 -07:00
Graham Campbell
3aacfb7849 Fixed the route service provider 2016-07-13 19:06:49 -07:00
Graham Campbell
2101b829bb Merge branch 'master' into develop 2016-07-13 18:41:58 -07:00
James Brooks
3913ab65ac Merge pull request #1962 from CachetHQ/user-management-api
Added Team Member API
2016-07-12 18:00:48 +01:00
James Brooks
2048e0899c Added Team Member API. Closes #1822 2016-07-12 17:59:50 +01:00
Graham Campbell
690290449e Merge branch 'master' into develop 2016-06-27 15:52:51 +01:00
Graham Campbell
e326381604 Merge branch 'master' into develop
# Conflicts:
#	composer.json
#	composer.lock
2016-06-11 00:55:48 +01:00
Graham Campbell
662fff9edf Merge branch 'master' into develop
# Conflicts:
#	VERSION
#	composer.json
#	composer.lock
2016-06-10 09:02:30 +01:00
Graham Campbell
c5c21cd8db Merge branch '2.3'
# Conflicts:
#	composer.lock
2016-06-07 11:21:25 +01:00
Graham Campbell
b960146512 Merge branch '2.3'
# Conflicts:
#	composer.json
#	composer.lock
2016-06-06 20:42:12 +01:00
Graham Campbell
3b6fb9f64e Use the new exception handler 2016-06-03 17:30:13 +01:00
Graham Campbell
acfb32bf1d Merge branch '2.3'
# Conflicts:
#	composer.json
#	composer.lock
2016-06-03 17:29:40 +01:00
James Brooks
f9946928c1 Merge pull request #1712 from CachetHQ/tags-api
Tags API
2016-06-02 11:25:14 +01:00
James Brooks
5176fca8f0 Implement the component tag API 2016-06-02 11:19:28 +01:00
James Brooks
4852f5558a Create a Tag api 2016-06-02 11:19:28 +01:00
Graham Campbell
b0c75a2319 Merge pull request #1863 from CachetHQ/analysis-8jl73Z
Applied fixes from StyleCI

[ci skip]
2016-05-30 12:15:20 +01:00
Graham Campbell
73d7303b6e Applied fixes from StyleCI
[ci skip]
2016-05-30 07:15:16 -04:00
Graham Campbell
2d8f2cbd63 Fixed typo 2016-05-30 12:14:56 +01:00
Graham Campbell
cb2ce8f67f Synced with laravel 5.3 2016-05-30 12:12:43 +01:00
Graham Campbell
d6d103cf15 Merge branch '2.3' 2016-05-29 19:38:59 +01:00
Graham Campbell
7f36783582 Merge branch '2.3' 2016-05-29 12:10:20 +01:00
Graham Campbell
ed851c232a Merge branch '2.3'
# Conflicts:
#	composer.lock
2016-05-28 22:07:52 +01:00
Graham Campbell
920a3ef6a9 Updated dependencies 2016-05-28 16:40:26 +01:00
Graham Campbell
15816c56bb Merge branch '2.3'
# Conflicts:
#	composer.json
#	composer.lock
2016-05-28 16:36:06 +01:00
Graham Campbell
e5e4b04151 Fix merge 2016-05-25 22:03:36 +01:00
Graham Campbell
283d343dd6 Merge branch '2.3'
# Conflicts:
#	composer.json
#	composer.lock
2016-05-25 22:03:20 +01:00
Graham Campbell
aa85c8708e Merge pull request #1742 from CachetHQ/next
Upgraded to Laravel 5.3
2016-05-25 19:54:46 +01:00
Graham Campbell
f93506fed9 Updated to the latest laravel version 2016-05-25 16:06:11 +01:00
Graham Campbell
b92f9d30fc Use the new bindings substitution middleware 2016-05-25 15:56:28 +01:00
Graham Campbell
3673e57c6f Upgraded to laravel 5.3 2016-05-25 12:38:16 +01:00
Graham Campbell
9fb6ae4fc0 Merge branch '2.3' 2016-05-25 12:28:46 +01:00
Graham Campbell
583601644c Merge branch '2.3'
# Conflicts:
#	composer.json
#	composer.lock
2016-05-25 12:04:37 +01:00
Graham Campbell
5ea816ce2b Merge pull request #1595 from CachetHQ/incident-updates
Incident updates
2016-05-14 08:47:03 +01:00
Joseph Cohen
541d410e41 Implement Incident Updates 2016-05-14 02:38:07 -05:00
Graham Campbell
03348b8ff2 Merge branch '2.3'
# Conflicts:
#	composer.lock
2016-05-13 00:38:09 +01:00
Graham Campbell
2e8743eb6b Merge branch '2.3' 2016-05-01 16:25:09 +01:00
Graham Campbell
66ff1cca65 Updated dependencies 2016-04-27 15:04:32 +01:00
Graham Campbell
64ff9b7d40 Updated phpunit and mockery 2016-04-27 15:01:48 +01:00
Graham Campbell
446aed6b9a Bumped min php version
# Conflicts:
#	composer.json
2016-04-27 15:01:30 +01:00
Graham Campbell
0281c44933 Bumped version 2016-04-27 15:00:45 +01:00
84 changed files with 3873 additions and 262 deletions

View File

@@ -1,7 +1,7 @@
Before submitting your issue, please make sure that you've checked the checkboxes below.
- [ ] I am running the [latest release](https://github.com/CachetHQ/Cachet/releases/latest) version of Cachet.
- [ ] I am running at least PHP 5.5.9. *You can check this by running `php -v`.*
- [ ] I am running at least PHP 5.6.4. *You can check this by running `php -v`.*
- [ ] I have ran `rm -rf bootstrap/cache/*`.
### Expected behaviour

View File

@@ -1,8 +1,6 @@
language: php
php:
- 5.5.9
- 5.5
- 5.6
- 7.0
- hhvm

View File

@@ -33,7 +33,7 @@ Use of `master` in a production environment is not recommended as it may change
## Requirements
- PHP 5.5.9+ or newer
- PHP 5.6.4+ or newer
- Apache or Nginx server
- [Composer](https://getcomposer.org)

View File

@@ -1 +1 @@
2.4.0-dev
3.0.0-dev

View File

@@ -0,0 +1,61 @@
<?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\ComponentTag;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Tag;
/**
* This is the add component tag command.
*
* @author James Brooks <james@alt-three.com>
*/
final class AddComponentTagCommand
{
/**
* The component to add the tag to.
*
* @var \CachetHQ\Cachet\Models\Component
*/
public $component;
/**
* The tag to add to the component.
*
* @var \CachetHQ\Cachet\Models\Tag
*/
public $tag;
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'component' => 'required',
'tag' => 'required',
];
/**
* Create a add component tag command instance.
*
* @param \CachetHQ\Cachet\Models\Component $component
* @param \CachetHQ\Cachet\Models\Tag $tag
*
* @return void
*/
public function __construct(Component $component, Tag $tag)
{
$this->component = $component;
$this->tag = $tag;
}
}

View File

@@ -0,0 +1,61 @@
<?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\ComponentTag;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Tag;
/**
* This is the delete component tag command.
*
* @author James Brooks <james@alt-three.com>
*/
final class DeleteComponentTagCommand
{
/**
* The component to delete the tag from.
*
* @var \CachetHQ\Cachet\Models\Component
*/
public $component;
/**
* The tag to delete.
*
* @var \CachetHQ\Cachet\Models\Tag
*/
public $tag;
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'component' => 'required',
'tag' => 'required',
];
/**
* Create a delete component tag command instance.
*
* @param \CachetHQ\Cachet\Models\Component $component
* @param \CachetHQ\Cachet\Models\Tag $tag
*
* @return void
*/
public function __construct(Component $component, Tag $tag)
{
$this->component = $component;
$this->tag = $tag;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\IncidentUpdate;
use CachetHQ\Cachet\Models\IncidentUpdate;
/**
* This is the remove incident update command.
*
* @author James Brooks <james@alt-three.com>
*/
final class RemoveIncidentUpdateCommand
{
/**
* The incident update to remove.
*
* @var \CachetHQ\Cachet\Models\IncidentUpdate
*/
public $incidentUpdate;
/**
* Create a new remove incident update command instance.
*
* @param \CachetHQ\Cachet\Models\IncidentUpdate $incidentUpdate
*
* @return void
*/
public function __construct(IncidentUpdate $incidentUpdate)
{
$this->incidentUpdate = $incidentUpdate;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\IncidentUpdate;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\User;
/**
* This is the report incident update command.
*
* @author James Brooks <james@alt-three.com>
*/
final class ReportIncidentUpdateCommand
{
/**
* The incident.
*
* @var \CachetHQ\Cachet\Models\Incident
*/
public $incident;
/**
* The incident status.
*
* @var int
*/
public $status;
/**
* The incident message.
*
* @var string
*/
public $message;
/**
* The user.
*
* @var \CachetHQ\Cachet\Models\User
*/
public $user;
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'incident' => 'required',
'status' => 'required|int|min:1|max:4',
'message' => 'required|string',
'user' => 'required',
];
/**
* Create a new report incident update command instance.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
* @param string $status
* @param string $message
* @param \CachetHQ\Cachet\Models\User $user
*
* @return void
*/
public function __construct(Incident $incident, $status, $message, User $user)
{
$this->incident = $incident;
$this->status = $status;
$this->message = $message;
$this->user = $user;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\IncidentUpdate;
use CachetHQ\Cachet\Models\IncidentUpdate;
use CachetHQ\Cachet\Models\User;
/**
* This is the update incident update command.
*
* @author James Brooks <james@alt-three.com>
*/
final class UpdateIncidentUpdateCommand
{
/**
* The incident update.
*
* @var \CachetHQ\Cachet\Models\IncidentUpdate
*/
public $update;
/**
* The incident status.
*
* @var int
*/
public $status;
/**
* The incident message.
*
* @var string
*/
public $message;
/**
* The user.
*
* @var \CachetHQ\Cachet\Models\User
*/
public $user;
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'update' => 'required',
'status' => 'int|min:1|max:4',
'message' => 'string',
'user' => 'required',
];
/**
* Create a new update incident update command instance.
*
* @param \CachetHQ\Cachet\Models\IncidentUpdate $update
* @param string $status
* @param string $message
* @param \CachetHQ\Cachet\Models\User $user
*
* @return void
*/
public function __construct(IncidentUpdate $update, $status, $message, User $user)
{
$this->update = $update;
$this->status = $status;
$this->message = $message;
$this->user = $user;
}
}

View File

@@ -0,0 +1,58 @@
<?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\Tag;
/**
* This is the create tag command.
*
* @author James Brooks <james@alt-three.com>
*/
final class CreateTagCommand
{
/**
* The name.
*
* @var string
*/
public $name;
/**
* The slug.
*
* @var string
*/
public $slug;
/**
* The validation rules.
*
* @var array
*/
public $rules = [
'name' => 'required|string',
'slug' => 'sometimes|string',
];
/**
* Create a new create tag command instance.
*
* @param string $name
* @param string $slug
*
* @return void
*/
public function __construct($name, $slug)
{
$this->name = $name;
$this->slug = $slug;
}
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\Tag;
use CachetHQ\Cachet\Models\Tag;
/**
* This is the delete tag command.
*
* @author James Brooks <james@alt-three.com>
*/
final class DeleteTagCommand
{
/**
* The tag.
*
* @var \CachetHQ\Cachet\Models\Tag
*/
public $tag;
/**
* The validation rules.
*
* @var array
*/
public $rules = [
'tag' => 'required',
];
/**
* Create a new delete tag command instance.
*
* @param \CachetHQ\Cachet\Models\Tag $tag
*
* @return void
*/
public function __construct(Tag $tag)
{
$this->tag = $tag;
}
}

View File

@@ -0,0 +1,70 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Commands\Tag;
use CachetHQ\Cachet\Models\Tag;
/**
* This is the update tag command.
*
* @author James Brooks <james@alt-three.com>
*/
final class UpdateTagCommand
{
/**
* The tag.
*
* @var \CachetHQ\Cachet\Models\Tag
*/
public $tag;
/**
* The name.
*
* @var string
*/
public $name;
/**
* The slug.
*
* @var string
*/
public $slug;
/**
* The validation rules.
*
* @var array
*/
public $rules = [
'tag' => 'required',
'name' => 'sometimes|string',
'slug' => 'sometimes|string',
];
/**
* Create a new update tag command instance.
*
* @param \CachetHQ\Cachet\Models\Tag $tag
* @param string $name
* @param string $slug
*
* @return void
*/
public function __construct(Tag $tag, $name, $slug)
{
$this->tag = $tag;
$this->name = $name;
$this->slug = $slug;
}
}

View File

@@ -53,8 +53,9 @@ final class AddUserCommand
*/
public $rules = [
'name' => 'required|string',
'password' => 'string',
'level' => 'int',
'email' => 'required|email',
'password' => 'required|string',
'level' => 'int|min:1|max:2',
];
/**

View File

@@ -0,0 +1,90 @@
<?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\User;
use CachetHQ\Cachet\Models\User;
/**
* This is the update team member command.
*
* @author James Brooks <james@alt-three.com>
*/
final class UpdateTeamMemberCommand
{
/**
* The user to update.
*
* @var \CachetHQ\Cachet\Models\User
*/
public $user;
/**
* The user username.
*
* @var string
*/
public $username;
/**
* The user password.
*
* @var string
*/
public $password;
/**
* The user email.
*
* @var string
*/
public $email;
/**
* The user level.
*
* @var int
*/
public $level;
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'user' => 'required',
'name' => 'required|string',
'email' => 'required|email',
'password' => 'required|string',
'level' => 'int|min:1|max:2',
];
/**
* Create a new add team member command instance.
*
* @param \CachetHQ\Cachet\Models\User $user
* @param string $username
* @param string $password
* @param string $email
* @param int $level
*
* @return void
*/
public function __construct(User $user, $username, $password, $email, $level)
{
$this->user = $user;
$this->username = $username;
$this->password = $password;
$this->email = $email;
$this->level = $level;
}
}

View File

@@ -0,0 +1,24 @@
<?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\Events\IncidentUpdate;
use CachetHQ\Cachet\Bus\Events\EventInterface;
/**
* This is the incident update event interface.
*
* @author James Brooks <james@alt-three.com>
*/
interface IncidentUpdateEventInterface extends EventInterface
{
//
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Events\IncidentUpdate;
use CachetHQ\Cachet\Models\IncidentUpdate;
/**
* This is the incident update was removed event.
*
* @author James Brooks <james@alt-three.com>
*/
final class IncidentUpdateWasRemovedEvent implements IncidentUpdateEventInterface
{
/**
* The incident update that has been removed.
*
* @var \CachetHQ\Cachet\Models\IncidentUpdate
*/
public $update;
/**
* Create a new incident update was removed event instance.
*
* @param \CachetHQ\Cachet\Models\IncidentUpdate $update
*
* @return void
*/
public function __construct(IncidentUpdate $update)
{
$this->update = $update;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Events\IncidentUpdate;
use CachetHQ\Cachet\Models\IncidentUpdate;
/**
* This is the incident update was reported event.
*
* @author James Brooks <james@alt-three.com>
*/
final class IncidentUpdateWasReportedEvent implements IncidentUpdateEventInterface
{
/**
* The incident update that has been reported.
*
* @var \CachetHQ\Cachet\Models\IncidentUpdate
*/
public $update;
/**
* Create a new incident update was reported event instance.
*
* @param \CachetHQ\Cachet\Models\IncidentUpdate $update
*
* @return void
*/
public function __construct(IncidentUpdate $update)
{
$this->update = $update;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Events\IncidentUpdate;
use CachetHQ\Cachet\Models\IncidentUpdate;
/**
* This is the incident update was updated event.
*
* @author James Brooks <james@alt-three.com>
*/
final class IncidentUpdateWasUpdatedEvent implements IncidentUpdateEventInterface
{
/**
* The incident update that has been updated.
*
* @var \CachetHQ\Cachet\Models\IncidentUpdate
*/
public $update;
/**
* Create a new incident update was updated event instance.
*
* @param \CachetHQ\Cachet\Models\IncidentUpdate $update
*
* @return void
*/
public function __construct(IncidentUpdate $update)
{
$this->update = $update;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Events\User;
use CachetHQ\Cachet\Models\User;
/**
* This is the user was updated event.
*
* @author James Brooks <james@alt-three.com>
*/
final class UserWasUpdatedEvent implements UserEventInterface
{
/**
* The user that has been updated.
*
* @var \CachetHQ\Cachet\Models\User
*/
public $user;
/**
* Create a new user was updated event instance.
*
* @param \CachetHQ\Cachet\Models\User $user
*
* @return void
*/
public function __construct(User $user)
{
$this->user = $user;
}
}

View File

@@ -0,0 +1,24 @@
<?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\Tag;
use Exception;
/**
* This is the tag does not exist on component exception class.
*
* @author James Brooks <james@alt-three.com>
*/
class TagDoesNotExistOnComponentException extends Exception implements TagExceptionInterface
{
//
}

View File

@@ -0,0 +1,24 @@
<?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\Tag;
use CachetHQ\Cachet\Bus\Exceptions\ExceptionInterface;
/**
* This is the tag exception interface.
*
* @author James Brooks <james@alt-three.com>
*/
interface TagExceptionInterface extends ExceptionInterface
{
//
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\ComponentTag;
use CachetHQ\Cachet\Bus\Commands\ComponentTag\AddComponentTagCommand;
use CachetHQ\Cachet\Models\ComponentTag;
/**
* This is the add component tag command handler.
*
* @author James Brooks <james@alt-three.com>
*/
class AddComponentTagCommandHandler
{
/**
* Handle the add component tag command.
*
* @param \CachetHQ\Cachet\Bus\Commands\ComponentTag\AddComponentTagCommand $command
*
* @return \CachetHQ\Cachet\Models\ComponentTag
*/
public function handle(AddComponentTagCommand $command)
{
$tag = ComponentTag::create([
'component_id' => $command->component->id,
'tag_id' => $command->tag->id,
]);
return $tag;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\ComponentTag;
use CachetHQ\Cachet\Bus\Commands\ComponentTag\DeleteComponentTagCommand;
use CachetHQ\Cachet\Bus\Exceptions\Tag\TagDoesNotExistOnComponentException;
use CachetHQ\Cachet\Models\ComponentTag;
/**
* This is the delete component tag command handler.
*
* @author James Brooks <james@alt-three.com>
*/
class DeleteComponentTagCommandHandler
{
/**
* Handle the add component tag command.
*
* @param \CachetHQ\Cachet\Bus\Commands\ComponentTag\DeleteComponentTagCommand $command
*
* @return void
*/
public function handle(DeleteComponentTagCommand $command)
{
// If the tag does not exist on the component, throw an exception.
if (!($componentTag = ComponentTag::tagForComponent($command->tag->id, $command->component->id))) {
throw new TagDoesNotExistOnComponentException('The given tag <'.$command->tag->id.'> does not exist on this component <'.$command->componnet->id.'>');
}
$componentTag->delete();
}
}

View File

@@ -0,0 +1,39 @@
<?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\IncidentUpdate;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\RemoveIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Events\IncidentUpdate\IncidentUpdateWasRemovedEvent;
/**
* This is the remove incident update command handler.
*
* @author James Brooks <james@alt-three.com>
*/
class RemoveIncidentUpdateCommandHandler
{
/**
* Handle the remove incident update command.
*
* @param \CachetHQ\Cachet\Bus\Commands\IncidentUpdate\RemoveIncidentUpdateCommand $command
*
* @return void
*/
public function handle(RemoveIncidentUpdateCommand $command)
{
$update = $command->incidentUpdate;
event(new IncidentUpdateWasRemovedEvent($update));
$update->delete();
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\IncidentUpdate;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\ReportIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Commands\Incident\UpdateIncidentCommand;
use CachetHQ\Cachet\Bus\Events\IncidentUpdate\IncidentUpdateWasReportedEvent;
use CachetHQ\Cachet\Models\IncidentUpdate;
/**
* This is the report incident update command handler.
*
* @author James Brooks <james@alt-three.com>
*/
class ReportIncidentUpdateCommandHandler
{
/**
* Handle the report incident command.
*
* @param \CachetHQ\Cachet\Bus\Commands\IncidentUpdate\ReportIncidentUpdateCommand $command
*
* @return \CachetHQ\Cachet\Models\IncidentUpdate
*/
public function handle(ReportIncidentUpdateCommand $command)
{
$data = [
'incident_id' => $command->incident->id,
'status' => $command->status,
'message' => $command->message,
'user_id' => $command->user->id,
];
// Create the incident update.
$update = IncidentUpdate::create($data);
// Update the original incident with the new status.
dispatch(new UpdateIncidentCommand(
$incident,
null,
$command->status,
null,
null,
null,
null,
null,
null,
null,
null
));
event(new IncidentUpdateWasReportedEvent($update));
return $update;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\IncidentUpdate;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\UpdateIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Events\IncidentUpdate\IncidentUpdateWasUpdatedEvent;
/**
* This is the update incident update command handler.
*
* @author James Brooks <james@alt-three.com>
*/
class UpdateIncidentUpdateCommandHandler
{
/**
* Handle the update incident update command.
*
* @param \CachetHQ\Cachet\Bus\Commands\IncidentUpdate\UpdateIncidentUpdateCommand $command
*
* @return \CachetHQ\Cachet\Models\IncidentUpdate
*/
public function handle(UpdateIncidentUpdateCommand $command)
{
$command->update->update($this->filter($command));
event(new IncidentUpdateWasUpdatedEvent($command->update));
return $command->update;
}
/**
* Filter the command data.
*
* @param \CachetHQ\Cachet\Bus\Commands\IncidentUpdate\UpdateIncidentUpdateCommand $command
*
* @return array
*/
protected function filter(UpdateIncidentUpdateCommand $command)
{
$params = [
'status' => $command->status,
'message' => $command->message,
'user_id' => $command->user->id,
];
return array_filter($params, function ($val) {
return $val !== null;
});
}
}

View File

@@ -0,0 +1,35 @@
<?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\Tag;
use CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand;
use CachetHQ\Cachet\Models\Tag;
class CreateTagCommandHandler
{
/**
* Handle the create tag command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand $command
*
* @return \CachetHQ\Cachet\Models\Tag
*/
public function handle(CreateTagCommand $command)
{
$tag = Tag::create([
'name' => $command->name,
'slug' => $command->slug,
]);
return $tag;
}
}

View File

@@ -0,0 +1,30 @@
<?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\Tag;
use CachetHQ\Cachet\Bus\Commands\Tag\DeleteTagCommand;
use CachetHQ\Cachet\Models\Tag;
class DeleteTagCommandHandler
{
/**
* Handle the delete tag command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Tag\DeleteTagCommand $command
*
* @return void
*/
public function handle(DeleteTagCommand $command)
{
$command->tag->delete();
}
}

View File

@@ -0,0 +1,51 @@
<?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\Tag;
use CachetHQ\Cachet\Bus\Commands\Tag\UpdateTagCommand;
use CachetHQ\Cachet\Models\Tag;
class UpdateTagCommandHandler
{
/**
* Handle the update tag command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Tag\UpdateTagCommand $command
*
* @return \CachetHQ\Cachet\Models\Tag
*/
public function handle(UpdateTagCommand $command)
{
$command->tag->update($this->filter($command));
return $command->tag;
}
/**
* Filter the command data.
*
* @param \CachetHQ\Cachet\Bus\Commands\Tag\UpdateTagCommand $command
*
* @return array
*/
protected function filter(UpdateTagCommand $command)
{
$params = [
'name' => $command->name,
'slug' => $command->slug,
];
return array_filter($params, function ($val) {
return $val !== null;
});
}
}

View File

@@ -0,0 +1,63 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Bus\Handlers\Commands\User;
use CachetHQ\Cachet\Bus\Commands\User\UpdateTeamMemberCommand;
use CachetHQ\Cachet\Bus\Events\User\UserWasUpdatedEvent;
use CachetHQ\Cachet\Models\User;
/**
* This is the update team member command handler.
*
* @author James Brooks <james@alt-three.com>
*/
class UpdateTeamMemberCommandHandler
{
/**
* Handle the add team member command.
*
* @param \CachetHQ\Cachet\Bus\Commands\User\UpdateTeamMemberCommand $command
*
* @return \CachetHQ\Cachet\Models\User
*/
public function handle(UpdateTeamMemberCommand $command)
{
$user = $command->user;
$user->update($this->filter($command));
event(new UserWasUpdatedEvent($user));
return $user;
}
/**
* Filter the command data.
*
* @param \CachetHQ\Cachet\Bus\Commands\User\UpdateTeamMemberCommand $command
*
* @return array
*/
protected function filter(UpdateTeamMemberCommand $command)
{
$params = [
'username' => $command->username,
'password' => $command->password,
'email' => $command->email,
'level' => $command->level,
];
return array_filter($params, function ($val) {
return $val !== null;
});
}
}

View File

@@ -15,6 +15,7 @@ use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\IncidentUpdate;
use CachetHQ\Cachet\Models\Metric;
use CachetHQ\Cachet\Models\MetricPoint;
use CachetHQ\Cachet\Models\Subscriber;
@@ -187,71 +188,31 @@ 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,
'name' => 'Our monkeys aren\'t performing',
'message' => 'We\'re investigating an issue with our monkeys not performing as they should be.',
'status' => Incident::INVESTIGATING,
'component_id' => 0,
'scheduled_at' => null,
'visible' => 1,
],
[
'name' => 'Awesome',
'message' => ':+1: We totally nailed the fix.',
'status' => 4,
], [
'name' => 'This is an unresolved incident',
'message' => 'Unresolved incidents are left without a **Fixed** update.',
'status' => Incident::INVESTIGATING,
'component_id' => 0,
'scheduled_at' => null,
'visible' => 1,
],
[
'name' => 'Monitoring the fix',
'message' => ":ship: We've deployed a fix.",
'status' => 3,
'component_id' => 0,
'scheduled_at' => null,
'visible' => 1,
],
[
'name' => 'Update',
'message' => "We've identified the problem. Our engineers are currently looking at it.",
'status' => 2,
'component_id' => 0,
'scheduled_at' => null,
'visible' => 1,
],
[
'name' => 'Test Incident',
'message' => 'Something went wrong, with something or another.',
'status' => 1,
'component_id' => 0,
'scheduled_at' => null,
'visible' => 1,
],
[
'name' => 'Investigating the API',
'message' => ':zap: We\'ve seen high response times from our API. It looks to be fixing itself as time goes on.',
'status' => 1,
'component_id' => 1,
'scheduled_at' => null,
'visible' => 1,
],
];
Incident::truncate();
IncidentUpdate::truncate();
foreach ($defaultIncidents as $incident) {
Incident::create($incident);
foreach ($defaultIncidents as $defaultIncident) {
$incident = Incident::create($defaultIncident);
$this->seedIncidentUpdates($incident);
}
}
@@ -265,6 +226,47 @@ EINCIDENT;
IncidentTemplate::truncate();
}
/**
* Seed the incident updates table for a given incident.
*
* @return void
*/
protected function seedIncidentUpdates($incident)
{
$defaultUpdates = [
1 => [
[
'status' => Incident::FIXED,
'message' => 'The monkeys are back and rested!',
'user_id' => 1,
], [
'status' => Incident::WATCHED,
'message' => 'Our monkeys need a break from performing. They\'ll be back after a good rest.',
'user_id' => 1,
], [
'status' => Incident::IDENTIFIED,
'message' => 'We have identified the issue with our lovely performing monkeys.',
'user_id' => 1,
],
],
2 => [
[
'status' => Incident::WATCHED,
'message' => 'We\'re actively watching this issue, so it remains unresolved.',
'user_id' => 1,
],
],
];
$updates = $defaultUpdates[$incident->id];
foreach ($updates as $updateId => $update) {
$update['incident_id'] = $incident->id;
IncidentUpdate::create($update);
}
}
/**
* Seed the metric points table.
*

View File

@@ -93,6 +93,9 @@ class EventServiceProvider extends ServiceProvider
'CachetHQ\Cachet\Bus\Events\User\UserWasInvitedEvent' => [
'CachetHQ\Cachet\Bus\Handlers\Events\User\SendInviteUserEmailHandler',
],
'CachetHQ\Cachet\Bus\Events\User\UserWasUpdatedEvent' => [
//
],
'CachetHQ\Cachet\Bus\Events\User\UserWasRemovedEvent' => [
//
],

View File

@@ -28,34 +28,36 @@ class RouteServiceProvider extends ServiceProvider
/**
* Define the route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
parent::boot();
$this->app->call([$this, 'bind']);
}
/**
* Define the bindings for the application.
*
* @param \Illuminate\Routing\Router $router
*
* @return void
*/
public function boot(Router $router)
public function bind(Router $router)
{
parent::boot($router);
$this->registerBindings();
}
/**
* Register model bindings.
*
* @return void
*/
protected function registerBindings()
{
$this->app->router->model('component', 'CachetHQ\Cachet\Models\Component');
$this->app->router->model('component_group', 'CachetHQ\Cachet\Models\ComponentGroup');
$this->app->router->model('incident', 'CachetHQ\Cachet\Models\Incident');
$this->app->router->model('incident_template', 'CachetHQ\Cachet\Models\IncidentTemplate');
$this->app->router->model('metric', 'CachetHQ\Cachet\Models\Metric');
$this->app->router->model('metric_point', 'CachetHQ\Cachet\Models\MetricPoint');
$this->app->router->model('setting', 'CachetHQ\Cachet\Models\Setting');
$this->app->router->model('subscriber', 'CachetHQ\Cachet\Models\Subscriber');
$this->app->router->model('subscription', 'CachetHQ\Cachet\Models\Subscription');
$this->app->router->model('user', 'CachetHQ\Cachet\Models\User');
$router->model('component', 'CachetHQ\Cachet\Models\Component');
$router->model('component_group', 'CachetHQ\Cachet\Models\ComponentGroup');
$router->model('component_tag', 'CachetHQ\Cachet\Models\ComponentTag');
$router->model('incident', 'CachetHQ\Cachet\Models\Incident');
$router->model('incident_template', 'CachetHQ\Cachet\Models\IncidentTemplate');
$router->model('metric', 'CachetHQ\Cachet\Models\Metric');
$router->model('metric_point', 'CachetHQ\Cachet\Models\MetricPoint');
$router->model('setting', 'CachetHQ\Cachet\Models\Setting');
$router->model('subscriber', 'CachetHQ\Cachet\Models\Subscriber');
$router->model('subscription', 'CachetHQ\Cachet\Models\Subscription');
$router->model('tag', 'CachetHQ\Cachet\Models\Tag');
$router->model('user', 'CachetHQ\Cachet\Models\User');
}
/**

View File

@@ -0,0 +1,117 @@
<?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\Http\Controllers\Api;
use CachetHQ\Cachet\Bus\Commands\ComponentTag\AddComponentTagCommand;
use CachetHQ\Cachet\Bus\Commands\ComponentTag\DeleteComponentTagCommand;
use CachetHQ\Cachet\Bus\Exceptions\Tag\TagDoesNotExistOnComponentException;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentTag;
use CachetHQ\Cachet\Models\Tag;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* This is the component tag controller class.
*
* @author James Brooks <james@alt-three.com>
*/
class ComponentTagController extends AbstractApiController
{
/**
* Get all tags.
*
* @return \Illuminate\Http\JsonResponse
*/
public function getTags()
{
$tags = Tag::query();
$tags->search(Binput::except(['sort', 'order', 'per_page']));
if ($sortBy = Binput::get('sort')) {
$direction = Binput::has('order') && Binput::get('order') == 'desc';
$tags->sort($sortBy, $direction);
}
$tags = $tags->paginate(Binput::get('per_page', 20));
return $this->paginator($tags, Request::instance());
}
/**
* Get a single component tag.
*
* @param \CachetHQ\Cachet\Models\ComponentTag $componentTag
*
* @return \Illuminate\Http\JsonResponse
*/
public function getTag(ComponentTag $componentTag)
{
return $this->item($componentTag);
}
/**
* Create a new component tag.
*
* @return \Illuminate\Http\JsonResponse
*/
public function postTags()
{
$component = Component::find(Binput::get('component_id'));
$tag = Tag::find(Binput::get('tag_id'));
if (!($component && $tag)) {
throw new BadRequestHttpException();
}
try {
$tag = dispatch(new AddComponentTagCommand(
$component,
$tag
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->item($tag);
}
/**
* Delete an existing tag.
*
* @return \Illuminate\Http\JsonResponse
*/
public function deleteTag()
{
$component = Component::find(Binput::get('component_id'));
$tag = Tag::find(Binput::get('tag_id'));
if (!($component && $tag)) {
throw new BadRequestHttpException();
}
try {
dispatch(new DeleteComponentTagCommand(
$component,
$tag
));
} catch (TagDoesNotExistOnComponentException $e) {
throw new BadRequestHttpException();
}
return $this->noContent();
}
}

View File

@@ -0,0 +1,132 @@
<?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\Http\Controllers\Api;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\RemoveIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\ReportIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\UpdateIncidentUpdateCommand;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentUpdate;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* This is the incident update controller.
*
* @author James Brooks <james@alt-three.com>
*/
class IncidentUpdateController extends AbstractApiController
{
/**
* Return all updates on the incident.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\Http\JsonResponse
*/
public function getIncidentUpdates(Incident $incident)
{
$updates = IncidentUpdate::orderBy('created_at', 'desc');
if ($sortBy = Binput::get('sort')) {
$direction = Binput::has('order') && Binput::get('order') == 'desc';
$updates->sort($sortBy, $direction);
}
$updates = $updates->paginate(Binput::get('per_page', 20));
return $this->paginator($updates, Request::instance());
}
/**
* Return a single incident update.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
* @param \CachetHQ\Cachet\Models\IncidentUpdate $update
*
* @return \Illuminate\Http\JsonResponse
*/
public function getIncidentUpdate(Incident $incident, IncidentUpdate $update)
{
return $this->item($update);
}
/**
* Create a new incident update.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\Http\JsonResponse
*/
public function postIncidentUpdate(Incident $incident)
{
try {
$update = dispatch(new ReportIncidentUpdateCommand(
$incident,
Binput::get('status'),
Binput::get('message'),
Auth::user()
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->item($update);
}
/**
* Update an incident update.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
* @param \CachetHQ\Cachet\Models\IncidentUpdate $update
*
* @return \Illuminate\Http\JsonResponse
*/
public function putIncidentUpdate(Incident $incident, IncidentUpdate $update)
{
try {
$update = dispatch(new UpdateIncidentUpdateCommand(
$update,
Binput::get('status'),
Binput::get('message'),
Auth::user()
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->item($update);
}
/**
* Create a new incident update.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
* @param \CachetHQ\Cachet\Models\IncidentUpdate $update
*
* @return \Illuminate\Http\JsonResponse
*/
public function deleteIncidentUpdate(Incident $incident, IncidentUpdate $update)
{
try {
dispatch(new RemoveIncidentUpdateCommand($update));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->noContent();
}
}

View File

@@ -0,0 +1,113 @@
<?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\Http\Controllers\Api;
use CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand;
use CachetHQ\Cachet\Bus\Commands\Tag\DeleteTagCommand;
use CachetHQ\Cachet\Bus\Commands\Tag\UpdateTagCommand;
use CachetHQ\Cachet\Models\Tag;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class TagController extends AbstractApiController
{
/**
* Get all tags.
*
* @return \Illuminate\Http\JsonResponse
*/
public function getTags()
{
$tags = Tag::query();
$tags->search(Binput::except(['sort', 'order', 'per_page']));
if ($sortBy = Binput::get('sort')) {
$direction = Binput::has('order') && Binput::get('order') == 'desc';
$tags->sort($sortBy, $direction);
}
$tags = $tags->paginate(Binput::get('per_page', 20));
return $this->paginator($tags, Request::instance());
}
/**
* Get a single tag.
*
* @param \CachetHQ\Cachet\Models\Tag $tag
*
* @return \Illuminate\Http\JsonResponse
*/
public function getTag(Tag $tag)
{
return $this->item($tag);
}
/**
* Create a new tag.
*
* @return \Illuminate\Http\JsonResponse
*/
public function postTags()
{
try {
$tag = dispatch(new CreateTagCommand(
Binput::get('name'),
Binput::get('slug')
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->item($tag);
}
/**
* Update an existing tag.
*
* @param \CachetHQ\Cachet\Models\Tag $tag
*
* @return \Illuminate\Http\JsonResponse
*/
public function putTag(Tag $tag)
{
try {
$tag = dispatch(new UpdateTagCommand(
$tag,
Binput::get('name'),
Binput::get('slug')
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->item($tag);
}
/**
* Delete an existing tag.
*
* @param \CachetHQ\Cachet\Models\Tag $tag
*
* @return \Illuminate\Http\JsonResponse
*/
public function deleteTag(Tag $tag)
{
dispatch(new DeleteTagCommand($tag));
return $this->noContent();
}
}

View File

@@ -0,0 +1,87 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Http\Controllers\Api;
use CachetHQ\Cachet\Bus\Commands\User\AddTeamMemberCommand;
use CachetHQ\Cachet\Bus\Commands\User\RemoveUserCommand;
use CachetHQ\Cachet\Bus\Commands\User\UpdateTeamMemberCommand;
use CachetHQ\Cachet\Models\User;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Database\QueryException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* This is the user controller class.
*
* @author James Brooks <james@alt-three.com>
*/
class UserController extends AbstractApiController
{
/**
* Create a new user.
*
* @return \Illuminate\Http\JsonResponse
*/
public function postUsers()
{
try {
$user = dispatch(new AddTeamMemberCommand(
Binput::get('username'),
Binput::get('password'),
Binput::get('email'),
Binput::get('level', User::LEVEL_USER)
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->item($user);
}
/**
* Update an existing user.
*
* @param \CachetHQ\Cachet\Models\User $user
*
* @return \Illuminate\Http\JsonResponse
*/
public function putUser(User $user)
{
try {
dispatch(new UpdateTeamMemberCommand(
$user,
Binput::get('username'),
Binput::get('password'),
Binput::get('email'),
Binput::get('level')
));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
return $this->item($user);
}
/**
* Delete a user from the system.
*
* @param \CachetHQ\Cachet\Models\User $user
*
* @return \Illuminate\Http\JsonResponse
*/
public function deleteUser(User $user)
{
dispatch(new RemoveUserCommand($user));
return $this->noContent();
}
}

View File

@@ -15,15 +15,22 @@ use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Bus\Commands\Incident\RemoveIncidentCommand;
use CachetHQ\Cachet\Bus\Commands\Incident\ReportIncidentCommand;
use CachetHQ\Cachet\Bus\Commands\Incident\UpdateIncidentCommand;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\ReportIncidentUpdateCommand;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\View;
/**
* This is the incident controller.
*
* @author James Brooks <james@alt-three.com>
*/
class IncidentController extends Controller
{
/**
@@ -33,13 +40,24 @@ class IncidentController extends Controller
*/
protected $subMenu = [];
/**
* The guard instance.
*
* @var \Illuminate\Contracts\Auth\Guard
*/
protected $auth;
/**
* Creates a new incident controller instance.
*
* @param \Illuminate\Contracts\Auth\Guard $auth
*
* @return void
*/
public function __construct()
public function __construct(Guard $auth)
{
$this->auth = $auth;
$this->subMenu = [
'incidents' => [
'title' => trans('dashboard.incidents.incidents'),
@@ -279,4 +297,43 @@ class IncidentController extends Controller
return Redirect::route('dashboard.templates.edit', ['id' => $template->id])
->withUpdatedTemplate($template);
}
/**
* Shows the incident update form.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\View\View
*/
public function showIncidentUpdateAction(Incident $incident)
{
return View::make('dashboard.incidents.update')->withIncident($incident);
}
/**
* Creates a new incident update.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\Http\RedirectResponse
*/
public function createIncidentUpdateAction(Incident $incident)
{
try {
$incident = dispatch(new ReportIncidentUpdateCommand(
$incident,
Binput::get('status'),
Binput::get('message'),
$this->auth->user()
));
} catch (ValidationException $e) {
return Redirect::route('dashboard.incidents.update', ['id' => $incident->id])
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.incidents.templates.edit.failure')))
->withErrors($e->getMessageBag());
}
return Redirect::route('dashboard.incidents.index')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.incidents.delete.success')));
}
}

View File

@@ -85,7 +85,7 @@ class StatusPageController extends AbstractApiController
$allIncidents = Incident::notScheduled()->where('visible', '>=', $incidentVisibility)->whereBetween('created_at', [
$startDate->copy()->subDays($daysToShow)->format('Y-m-d').' 00:00:00',
$startDate->format('Y-m-d').' 23:59:59',
])->orderBy('scheduled_at', 'desc')->orderBy('created_at', 'desc')->get()->groupBy(function (Incident $incident) {
])->orderBy('scheduled_at', 'desc')->orderBy('created_at', 'desc')->get()->load('updates')->groupBy(function (Incident $incident) {
return app(DateFactory::class)->make($incident->is_scheduled ? $incident->scheduled_at : $incident->created_at)->toDateString();
});

View File

@@ -37,11 +37,13 @@ class Kernel extends HttpKernel
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'Illuminate\Foundation\Http\Middleware\VerifyCsrfToken',
'Illuminate\Routing\Middleware\SubstituteBindings',
],
'api' => [
'Barryvdh\Cors\HandleCors',
'CachetHQ\Cachet\Http\Middleware\Acceptable',
'CachetHQ\Cachet\Http\Middleware\Timezone',
'Illuminate\Routing\Middleware\SubstituteBindings',
],
];

View File

@@ -38,14 +38,22 @@ class ApiRoutes
$router->get('components', 'ComponentController@getComponents');
$router->get('components/groups', 'ComponentGroupController@getGroups');
$router->get('components/groups/{component_group}', 'ComponentGroupController@getGroup');
$router->get('components/tags', 'ComponentTagController@getTags');
$router->get('components/tags/{component_tag}', 'ComponentTagController@getTag');
$router->get('components/{component}', 'ComponentController@getComponent');
$router->get('incidents', 'IncidentController@getIncidents');
$router->get('incidents/{incident}', 'IncidentController@getIncident');
$router->get('incidents/{incident}/updates', 'IncidentUpdateController@getIncidentUpdates');
$router->get('incidents/{incident}/updates/{update}', 'IncidentUpdateController@getIncidentUpdate');
$router->get('metrics', 'MetricController@getMetrics');
$router->get('metrics/{metric}', 'MetricController@getMetric');
$router->get('metrics/{metric}/points', 'MetricController@getMetricPoints');
$router->get('tags', 'TagController@getTags');
$router->get('tags/{tag}', 'TagController@getTag');
});
$router->group(['middleware' => ['auth.api:true']], function (Registrar $router) {
@@ -53,24 +61,35 @@ class ApiRoutes
$router->post('components', 'ComponentController@postComponents');
$router->post('components/groups', 'ComponentGroupController@postGroups');
$router->post('components/tags', 'ComponentTagController@postTags');
$router->post('incidents', 'IncidentController@postIncidents');
$router->post('incidents/{incident}/updates', 'IncidentUpdateController@postIncidentUpdate');
$router->post('metrics', 'MetricController@postMetrics');
$router->post('metrics/{metric}/points', 'MetricPointController@postMetricPoints');
$router->post('subscribers', 'SubscriberController@postSubscribers');
$router->post('tags', 'TagController@postTags');
$router->post('users', 'UserController@postUsers');
$router->put('components/groups/{component_group}', 'ComponentGroupController@putGroup');
$router->put('components/{component}', 'ComponentController@putComponent');
$router->put('incidents/{incident}', 'IncidentController@putIncident');
$router->put('incidents/{incident}/updates/{update}', 'IncidentUpdateController@putIncidentUpdate');
$router->put('metrics/{metric}', 'MetricController@putMetric');
$router->put('metrics/{metric}/points/{metric_point}', 'MetricPointController@putMetricPoint');
$router->put('tags/{tag}', 'TagController@putTag');
$router->put('users/{user}', 'UserController@putUser');
$router->delete('components/groups/{component_group}', 'ComponentGroupController@deleteGroup');
$router->delete('components/tags', 'ComponentTagController@deleteTag');
$router->delete('components/{component}', 'ComponentController@deleteComponent');
$router->delete('incidents/{incident}', 'IncidentController@deleteIncident');
$router->delete('incidents/{incident}/updates/{update}', 'IncidentUpdateController@deleteIncidentUpdate');
$router->delete('metrics/{metric}', 'MetricController@deleteMetric');
$router->delete('metrics/{metric}/points/{metric_point}', 'MetricPointController@deleteMetricPoint');
$router->delete('subscribers/{subscriber}', 'SubscriberController@deleteSubscriber');
$router->delete('subscriptions/{subscription}', 'SubscriberController@deleteSubscription');
$router->delete('tags/{tag}', 'TagController@deleteTag');
$router->delete('users/{user}', 'UserController@deleteUser');
});
});
}

View File

@@ -89,7 +89,12 @@ class DashboardRoutes
'as' => 'edit',
'uses' => 'IncidentController@showEditIncidentAction',
]);
$router->get('{incident}/update', [
'as' => 'update',
'uses' => 'IncidentController@showIncidentUpdateAction',
]);
$router->post('{incident}/edit', 'IncidentController@editIncidentAction');
$router->post('{incident}/update', 'IncidentController@createIncidentUpdateAction');
});
$router->group(['as' => 'schedule.', 'prefix' => 'schedule'], function (Registrar $router) {

View File

@@ -53,8 +53,11 @@ class System implements SystemContract
return $incident->status > 0;
});
$incidentCount = $incidents->count();
$unresolvedCount = $incidents->filter(function ($incident) {
return !$incident->is_resolved;
})->count();
if ($incidentCount === 0 || ($incidentCount >= 1 && (int) $incidents->first()->status === 4)) {
if ($incidentCount === 0 || ($incidentCount >= 1 && $unresolvedCount === 0)) {
$status = [
'system_status' => 'success',
'system_message' => trans_choice('cachet.service.good', $totalComponents),

View File

@@ -129,11 +129,11 @@ class Component extends Model implements HasPresenter
/**
* Components can have many tags.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
public function tags()
{
return $this->belongsToMany(Tag::class);
return $this->hasManyThrough(Tag::class, ComponentTag::class, 'tag_id', 'id');
}
/**

View File

@@ -0,0 +1,74 @@
<?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;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
/**
* This is the component tag class.
*
* @author James Brooks <james@alt-three.com>
*/
class ComponentTag extends Model
{
/**
* The attributes that should be casted to native types.
*
* @var string[]
*/
protected $casts = [
'component_id' => 'int',
'tag_id' => 'int',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = ['component_id', 'tag_id'];
/**
* Get the component relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function component()
{
return $this->belongsTo(Component::class, 'component_id', 'id');
}
/**
* Get the tag relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function tag()
{
return $this->belongsTo(Tag::class, 'tag_id', 'id');
}
/**
* Find a given tag for a component.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param int $tagId
* @param int $componentId
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeTagForComponent(Builder $query, $tagId, $componentId)
{
return $query->where('component_id', $componentId)->where('tag_id', $tagId);
}
}

View File

@@ -25,6 +25,43 @@ class Incident extends Model implements HasPresenter
{
use SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
/**
* Status for incident being investigated.
*
* @var int
*/
const INVESTIGATING = 1;
/**
* Status for incident having been identified.
*
* @var int
*/
const IDENTIFIED = 2;
/**
* Status for incident being watched.
*
* @var int
*/
const WATCHED = 3;
/**
* Status for incident now being fixed.
*
* @var int
*/
const FIXED = 4;
/**
* The accessors to append to the model's array form.
*
* @var string[]
*/
protected $appends = [
'is_resolved',
];
/**
* The attributes that should be casted to native types.
*
@@ -91,6 +128,13 @@ class Incident extends Model implements HasPresenter
'message',
];
/**
* The relations to eager load on every query.
*
* @var string[]
*/
protected $with = ['updates'];
/**
* Finds all visible incidents.
*
@@ -131,6 +175,16 @@ class Incident extends Model implements HasPresenter
});
}
/**
* Get the updates relation.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function updates()
{
return $this->hasMany(IncidentUpdate::class)->orderBy('created_at', 'desc');
}
/**
* An incident belongs to a component.
*
@@ -141,6 +195,20 @@ class Incident extends Model implements HasPresenter
return $this->belongsTo(Component::class, 'component_id', 'id');
}
/**
* Is the incident resolved?
*
* @return bool
*/
public function getIsResolvedAttribute()
{
if ($updates = $this->updates->first()) {
return $updates->status === self::FIXED;
}
return $this->status === self::FIXED;
}
/**
* Returns whether the "incident" is scheduled or not.
*

View File

@@ -0,0 +1,90 @@
<?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;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use CachetHQ\Cachet\Presenters\IncidentUpdatePresenter;
use Illuminate\Database\Eloquent\Model;
use McCool\LaravelAutoPresenter\HasPresenter;
class IncidentUpdate extends Model implements HasPresenter
{
use SortableTrait, ValidatingTrait;
/**
* The attributes that should be casted to native types.
*
* @var string[]
*/
protected $casts = [
'incident_id' => 'int',
'status' => 'int',
'message' => 'string',
'user_id' => 'int',
];
/**
* The fillable properties.
*
* @var string[]
*/
protected $fillable = [
'incident_id',
'status',
'message',
'user_id',
];
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'incident_id' => 'int',
'status' => 'required|int',
'message' => 'required|string',
'user_id' => 'required|int',
];
/**
* The sortable fields.
*
* @var string[]
*/
protected $sortable = [
'id',
'status',
'user_id',
];
/**
* Get the incident relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function incident()
{
return $this->belongsTo(Incident::class);
}
/**
* Get the presenter class.
*
* @return string
*/
public function getPresenterClass()
{
return IncidentUpdatePresenter::class;
}
}

View File

@@ -11,11 +11,15 @@
namespace CachetHQ\Cachet\Models;
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Tag extends Model
{
use SearchableTrait, SortableTrait;
/**
* The attributes that should be casted to native types.
*
@@ -32,6 +36,28 @@ class Tag extends Model
*/
protected $fillable = ['name'];
/**
* The searchable fields.
*
* @var string[]
*/
protected $searchable = [
'id',
'name',
'slug',
];
/**
* The sortable fields.
*
* @var string[]
*/
protected $sortable = [
'id',
'name',
'slug',
];
/**
* Overrides the models boot method.
*/

View File

@@ -22,6 +22,19 @@ class IncidentPresenter extends BasePresenter implements Arrayable
{
use TimestampsTrait;
/**
* Inciden icon lookup.
*
* @var array
*/
protected $icons = [
0 => 'icon ion-android-calendar', // Scheduled
1 => 'icon ion-flag oranges', // Investigating
2 => 'icon ion-alert yellows', // Identified
3 => 'icon ion-eye blues', // Watching
4 => 'icon ion-checkmark greens', // Fixed
];
/**
* Renders the message from Markdown into HTML.
*
@@ -32,6 +45,16 @@ class IncidentPresenter extends BasePresenter implements Arrayable
return Markdown::convertToHtml($this->wrappedObject->message);
}
/**
* Return the raw text of the message, even without Markdown.
*
* @return string
*/
public function raw_message()
{
return strip_tags($this->formattedMessage());
}
/**
* Present diff for humans date time.
*
@@ -157,19 +180,8 @@ class IncidentPresenter extends BasePresenter implements Arrayable
*/
public function icon()
{
switch ($this->wrappedObject->status) {
case 0: // Scheduled
return 'icon ion-android-calendar';
case 1: // Investigating
return 'icon ion-flag oranges';
case 2: // Identified
return 'icon ion-alert yellows';
case 3: // Watching
return 'icon ion-eye blues';
case 4: // Fixed
return 'icon ion-checkmark greens';
default: // Something actually broke, this shouldn't happen.
return '';
if (isset($this->icons[$this->wrappedObject->status])) {
return $this->icons[$this->wrappedObject->status];
}
}
@@ -183,6 +195,72 @@ class IncidentPresenter extends BasePresenter implements Arrayable
return trans('cachet.incidents.status.'.$this->wrappedObject->status);
}
/**
* Returns the latest update.
*
* @return int|null
*/
public function latest_status()
{
if ($update = $this->latest()) {
return $update->status;
}
return $this->wrappedObject->status;
}
/**
* Returns the latest update.
*
* @return string|null
*/
public function latest_human_status()
{
if ($update = $this->latest()) {
return trans('cachet.incidents.status.'.$update->status);
}
return $this->human_status();
}
/**
* Present the latest icon.
*
* @return string
*/
public function latest_icon()
{
if ($update = $this->latest()) {
if (isset($this->icons[$update->status])) {
return $this->icons[$update->status];
}
}
return $this->icon();
}
/**
* Fetch the latest incident update.
*
* @return \CachetHQ\Cachet\Models\IncidentUpdate|null
*/
public function latest()
{
if ($update = $this->wrappedObject->updates()->orderBy('created_at', 'desc')->first()) {
return $update;
}
}
/**
* Get the incident permalink.
*
* @return string
*/
public function permalink()
{
return route('incident', $this->wrappedObject->id);
}
/**
* Convert the presenter instance to an array.
*
@@ -191,10 +269,15 @@ class IncidentPresenter extends BasePresenter implements Arrayable
public function toArray()
{
return array_merge($this->wrappedObject->toArray(), [
'human_status' => $this->human_status(),
'scheduled_at' => $this->scheduled_at(),
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),
'human_status' => $this->human_status(),
'latest_update_id' => $this->latest() ? $this->latest()->id : null,
'latest_status' => $this->latest_status(),
'latest_human_status' => $this->latest_human_status(),
'latest_icon' => $this->latest_icon(),
'permalink' => $this->permalink(),
'scheduled_at' => $this->scheduled_at(),
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),
]);
}
}

View File

@@ -0,0 +1,162 @@
<?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\Presenters;
use CachetHQ\Cachet\Dates\DateFactory;
use CachetHQ\Cachet\Presenters\Traits\TimestampsTrait;
use GrahamCampbell\Markdown\Facades\Markdown;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Config;
use McCool\LaravelAutoPresenter\BasePresenter;
/**
* This is the incident update presenter.
*
* @author James Brooks <james@alt-three.com>
*/
class IncidentUpdatePresenter extends BasePresenter implements Arrayable
{
use TimestampsTrait;
/**
* Renders the message from Markdown into HTML.
*
* @return string
*/
public function formattedMessage()
{
return Markdown::convertToHtml($this->wrappedObject->message);
}
/**
* Return the raw text of the message, even without Markdown.
*
* @return string
*/
public function raw_message()
{
return strip_tags($this->formattedMessage());
}
/**
* Present diff for humans date time.
*
* @return string
*/
public function created_at_diff()
{
return app(DateFactory::class)->make($this->wrappedObject->created_at)->diffForHumans();
}
/**
* Present formatted date time.
*
* @return string
*/
public function created_at_formatted()
{
return ucfirst(app(DateFactory::class)->make($this->wrappedObject->created_at)->format(Config::get('setting.incident_date_format', 'l jS F Y H:i:s')));
}
/**
* Formats the created_at time ready to be used by bootstrap-datetimepicker.
*
* @return string
*/
public function created_at_datetimepicker()
{
return app(DateFactory::class)->make($this->wrappedObject->created_at)->format('d/m/Y H:i');
}
/**
* Present formatted date time.
*
* @return string
*/
public function created_at_iso()
{
return app(DateFactory::class)->make($this->wrappedObject->created_at)->toISO8601String();
}
/**
* Returns a formatted timestamp for use within the timeline.
*
* @return string
*/
public function timestamp_formatted()
{
if ($this->wrappedObject->is_scheduled) {
return $this->scheduled_at_formatted;
}
return $this->created_at_formatted;
}
/**
* Return the iso timestamp for use within the timeline.
*
* @return string
*/
public function timestamp_iso()
{
if ($this->wrappedObject->is_scheduled) {
return $this->scheduled_at_iso;
}
return $this->created_at_iso;
}
/**
* Present the status with an icon.
*
* @return string
*/
public function icon()
{
switch ($this->wrappedObject->status) {
case 1: // Investigating
return 'icon ion-flag oranges';
case 2: // Identified
return 'icon ion-alert yellows';
case 3: // Watching
return 'icon ion-eye blues';
case 4: // Fixed
return 'icon ion-checkmark greens';
default: // Something actually broke, this shouldn't happen.
return '';
}
}
/**
* Returns a human readable version of the status.
*
* @return string
*/
public function human_status()
{
return trans('cachet.incidents.status.'.$this->wrappedObject->status);
}
/**
* Convert the presenter instance to an array.
*
* @return string[]
*/
public function toArray()
{
return array_merge($this->wrappedObject->toArray(), [
'human_status' => $this->human_status(),
'created_at' => $this->created_at(),
'updated_at' => $this->updated_at(),
]);
}
}

View File

@@ -26,7 +26,7 @@ $app->singleton('Illuminate\Contracts\Http\Kernel', 'CachetHQ\Cachet\Http\Kernel
$app->singleton('Illuminate\Contracts\Console\Kernel', 'CachetHQ\Cachet\Console\Kernel');
$app->singleton('Illuminate\Contracts\Debug\ExceptionHandler', 'GrahamCampbell\Exceptions\ExceptionHandler');
$app->singleton('Illuminate\Contracts\Debug\ExceptionHandler', 'GrahamCampbell\Exceptions\NewExceptionHandler');
/*
|--------------------------------------------------------------------------

View File

@@ -19,8 +19,8 @@
}
],
"require": {
"php": ">=5.5.9",
"laravel/framework": "5.2.39",
"php": ">=5.6.4",
"laravel/framework": "~5.3.0",
"alt-three/badger": "^3.1",
"alt-three/bus": "^1.1",
"alt-three/emoji": "^3.1",
@@ -28,7 +28,7 @@
"alt-three/validator": "^1.5",
"aws/aws-sdk-php": "^3.7",
"backup-manager/laravel": "^1.1",
"barryvdh/laravel-cors": "^0.8",
"barryvdh/laravel-cors": "^0.8.1",
"doctrine/dbal": "^2.5",
"fedeisas/laravel-mail-css-inliner": "^1.5",
"fideloper/proxy": "^3.1",
@@ -40,7 +40,7 @@
"jenssegers/date": "^3.2",
"mccool/laravel-auto-presenter": "^4.3",
"pragmarx/google2fa": "^0.7.1",
"rcrowe/twigbridge": "^0.9.2",
"rcrowe/twigbridge": "^0.10",
"roumen/feed": "^2.10.4"
},
"require-dev": {
@@ -49,9 +49,9 @@
"fzaninotto/faker": "^1.6",
"graham-campbell/testbench-core": "^1.1",
"mockery/mockery": "0.9.5",
"phpunit/phpunit": "4.8.21",
"symfony/css-selector": "^3.0",
"symfony/dom-crawler": "^3.0"
"phpunit/phpunit": "^5.2",
"symfony/css-selector": "^3.1",
"symfony/dom-crawler": "^3.1"
},
"autoload": {
"classmap": [
@@ -92,13 +92,13 @@
},
"config": {
"platform": {
"php": "5.5.9"
"php": "5.6.4"
},
"preferred-install": "dist"
},
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"dev-develop": "3.0-dev"
}
},
"minimum-stability": "dev",

466
composer.lock generated
View File

@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "bebdff554746dd7e0b24b4881e92191e",
"content-hash": "7c21d245d3c8c6f69ac1d98a58e4b761",
"hash": "59d3efdb1e49d5508a73a3ee84ecb741",
"content-hash": "0ede4f4a11e39a6f3c9bec3a5478a91e",
"packages": [
{
"name": "alt-three/badger",
@@ -2019,16 +2019,16 @@
},
{
"name": "laravel/framework",
"version": "v5.2.39",
"version": "5.3.x-dev",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "c2a77050269b4e03bd9a735a9f24e573a7598b8a"
"reference": "81c4428924fd565bfaca3941402be29cd384f8d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/c2a77050269b4e03bd9a735a9f24e573a7598b8a",
"reference": "c2a77050269b4e03bd9a735a9f24e573a7598b8a",
"url": "https://api.github.com/repos/laravel/framework/zipball/81c4428924fd565bfaca3941402be29cd384f8d4",
"reference": "81c4428924fd565bfaca3941402be29cd384f8d4",
"shasum": ""
},
"require": {
@@ -2041,20 +2041,19 @@
"monolog/monolog": "~1.11",
"mtdowling/cron-expression": "~1.0",
"nesbot/carbon": "~1.20",
"paragonie/random_compat": "~1.4",
"php": ">=5.5.9",
"paragonie/random_compat": "~1.4|~2.0",
"php": ">=5.6.4",
"psy/psysh": "0.7.*",
"swiftmailer/swiftmailer": "~5.1",
"symfony/console": "2.8.*|3.0.*",
"symfony/debug": "2.8.*|3.0.*",
"symfony/finder": "2.8.*|3.0.*",
"symfony/http-foundation": "2.8.*|3.0.*",
"symfony/http-kernel": "2.8.*|3.0.*",
"symfony/polyfill-php56": "~1.0",
"symfony/process": "2.8.*|3.0.*",
"symfony/routing": "2.8.*|3.0.*",
"symfony/translation": "2.8.*|3.0.*",
"symfony/var-dumper": "2.8.*|3.0.*",
"symfony/console": "3.1.*",
"symfony/debug": "3.1.*",
"symfony/finder": "3.1.*",
"symfony/http-foundation": "3.1.*",
"symfony/http-kernel": "3.1.*",
"symfony/process": "3.1.*",
"symfony/routing": "3.1.*",
"symfony/translation": "3.1.*",
"symfony/var-dumper": "3.1.*",
"vlucas/phpdotenv": "~2.2"
},
"replace": {
@@ -2085,16 +2084,17 @@
"illuminate/support": "self.version",
"illuminate/translation": "self.version",
"illuminate/validation": "self.version",
"illuminate/view": "self.version"
"illuminate/view": "self.version",
"tightenco/collect": "self.version"
},
"require-dev": {
"aws/aws-sdk-php": "~3.0",
"mockery/mockery": "~0.9.4",
"pda/pheanstalk": "~3.0",
"phpunit/phpunit": "~4.1",
"phpunit/phpunit": "~5.4",
"predis/predis": "~1.0",
"symfony/css-selector": "2.8.*|3.0.*",
"symfony/dom-crawler": "2.8.*|3.0.*"
"symfony/css-selector": "3.1.*",
"symfony/dom-crawler": "3.1.*"
},
"suggest": {
"aws/aws-sdk-php": "Required to use the SQS queue driver and SES mail driver (~3.0).",
@@ -2106,20 +2106,17 @@
"pda/pheanstalk": "Required to use the beanstalk queue driver (~3.0).",
"predis/predis": "Required to use the redis cache and queue drivers (~1.0).",
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (~2.0).",
"symfony/css-selector": "Required to use some of the crawler integration testing tools (2.8.*|3.0.*).",
"symfony/dom-crawler": "Required to use most of the crawler integration testing tools (2.8.*|3.0.*).",
"symfony/css-selector": "Required to use some of the crawler integration testing tools (3.1.*).",
"symfony/dom-crawler": "Required to use most of the crawler integration testing tools (3.1.*).",
"symfony/psr-http-message-bridge": "Required to psr7 bridging features (0.2.*)."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.2-dev"
"dev-master": "5.3-dev"
}
},
"autoload": {
"classmap": [
"src/Illuminate/Queue/IlluminateQueueClosure.php"
],
"files": [
"src/Illuminate/Foundation/helpers.php",
"src/Illuminate/Support/helpers.php"
@@ -2144,7 +2141,7 @@
"framework",
"laravel"
],
"time": "2016-06-17 19:25:12"
"time": "2016-07-29 13:52:53"
},
{
"name": "league/commonmark",
@@ -2637,16 +2634,16 @@
},
{
"name": "paragonie/random_compat",
"version": "v1.4.1",
"version": "v2.0.2",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "c7e26a21ba357863de030f0b9e701c7d04593774"
"reference": "088c04e2f261c33bed6ca5245491cfca69195ccf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/c7e26a21ba357863de030f0b9e701c7d04593774",
"reference": "c7e26a21ba357863de030f0b9e701c7d04593774",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/088c04e2f261c33bed6ca5245491cfca69195ccf",
"reference": "088c04e2f261c33bed6ca5245491cfca69195ccf",
"shasum": ""
},
"require": {
@@ -2681,7 +2678,7 @@
"pseudorandom",
"random"
],
"time": "2016-03-18 20:34:03"
"time": "2016-04-03 06:00:07"
},
{
"name": "pragmarx/google2fa",
@@ -2898,21 +2895,21 @@
},
{
"name": "rcrowe/twigbridge",
"version": "v0.9.2",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/rcrowe/TwigBridge.git",
"reference": "78a4b7da75042660258a3269751c259eee81c34a"
"reference": "6226d33331bbb1cdf64593a786f7efd1670200a7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/78a4b7da75042660258a3269751c259eee81c34a",
"reference": "78a4b7da75042660258a3269751c259eee81c34a",
"url": "https://api.github.com/repos/rcrowe/TwigBridge/zipball/6226d33331bbb1cdf64593a786f7efd1670200a7",
"reference": "6226d33331bbb1cdf64593a786f7efd1670200a7",
"shasum": ""
},
"require": {
"illuminate/support": "5.0.*|5.1.*|5.2.*",
"illuminate/view": "5.0.*|5.1.*|5.2.*",
"illuminate/support": "5.0.*|5.1.*|5.2.*|5.3.*",
"illuminate/view": "5.0.*|5.1.*|5.2.*|5.3.*",
"php": ">=5.4.0",
"twig/twig": "~1.15|~2.0"
},
@@ -2958,7 +2955,7 @@
"laravel",
"twig"
],
"time": "2016-02-22 07:48:48"
"time": "2016-05-01 16:43:38"
},
{
"name": "roumen/feed",
@@ -3117,16 +3114,16 @@
},
{
"name": "symfony/console",
"version": "v3.0.8",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "a7abb7153f6d1da47f87ec50274844e246b09d9f"
"reference": "747154aa69b0f83cd02fc9aa554836dee417631a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/a7abb7153f6d1da47f87ec50274844e246b09d9f",
"reference": "a7abb7153f6d1da47f87ec50274844e246b09d9f",
"url": "https://api.github.com/repos/symfony/console/zipball/747154aa69b0f83cd02fc9aa554836dee417631a",
"reference": "747154aa69b0f83cd02fc9aa554836dee417631a",
"shasum": ""
},
"require": {
@@ -3146,7 +3143,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
},
"autoload": {
@@ -3173,7 +3170,7 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2016-06-29 07:02:21"
"time": "2016-06-29 07:02:31"
},
{
"name": "symfony/css-selector",
@@ -3230,16 +3227,16 @@
},
{
"name": "symfony/debug",
"version": "v3.0.8",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
"reference": "c54bc3539c3b87e86799533801e8ae0e971d78c2"
"reference": "06e2d52e307ef880ac739f44ee6c2418ca9e3283"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/debug/zipball/c54bc3539c3b87e86799533801e8ae0e971d78c2",
"reference": "c54bc3539c3b87e86799533801e8ae0e971d78c2",
"url": "https://api.github.com/repos/symfony/debug/zipball/06e2d52e307ef880ac739f44ee6c2418ca9e3283",
"reference": "06e2d52e307ef880ac739f44ee6c2418ca9e3283",
"shasum": ""
},
"require": {
@@ -3256,7 +3253,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
},
"autoload": {
@@ -3283,7 +3280,7 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
"time": "2016-06-29 05:40:00"
"time": "2016-06-29 05:41:56"
},
{
"name": "symfony/event-dispatcher",
@@ -3347,16 +3344,16 @@
},
{
"name": "symfony/finder",
"version": "v3.0.8",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "3eb4e64c6145ef8b92adefb618a74ebdde9e3fe9"
"reference": "8201978de88a9fa0923e18601bb17f1df9c721e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/3eb4e64c6145ef8b92adefb618a74ebdde9e3fe9",
"reference": "3eb4e64c6145ef8b92adefb618a74ebdde9e3fe9",
"url": "https://api.github.com/repos/symfony/finder/zipball/8201978de88a9fa0923e18601bb17f1df9c721e7",
"reference": "8201978de88a9fa0923e18601bb17f1df9c721e7",
"shasum": ""
},
"require": {
@@ -3365,7 +3362,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
},
"autoload": {
@@ -3392,20 +3389,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2016-06-29 05:40:00"
"time": "2016-06-29 05:41:56"
},
{
"name": "symfony/http-foundation",
"version": "v3.0.8",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "1341139f906d295baa4f4abd55293d07e25a065a"
"reference": "6b5a3764c5b0ce1f0ec3b8be5bba969122a78cfc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/1341139f906d295baa4f4abd55293d07e25a065a",
"reference": "1341139f906d295baa4f4abd55293d07e25a065a",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/6b5a3764c5b0ce1f0ec3b8be5bba969122a78cfc",
"reference": "6b5a3764c5b0ce1f0ec3b8be5bba969122a78cfc",
"shasum": ""
},
"require": {
@@ -3418,7 +3415,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
},
"autoload": {
@@ -3445,20 +3442,20 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
"time": "2016-06-29 07:02:21"
"time": "2016-06-29 07:02:31"
},
{
"name": "symfony/http-kernel",
"version": "v3.0.8",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "177b63b2d50b63fa6d82ea41359ed9928cc7a1fb"
"reference": "3a1ce1829128988826739bd9dd820f3238b92d65"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/177b63b2d50b63fa6d82ea41359ed9928cc7a1fb",
"reference": "177b63b2d50b63fa6d82ea41359ed9928cc7a1fb",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/3a1ce1829128988826739bd9dd820f3238b92d65",
"reference": "3a1ce1829128988826739bd9dd820f3238b92d65",
"shasum": ""
},
"require": {
@@ -3500,7 +3497,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
},
"autoload": {
@@ -3527,7 +3524,7 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com",
"time": "2016-06-30 16:30:17"
"time": "2016-06-30 17:16:01"
},
{
"name": "symfony/polyfill-mbstring",
@@ -3698,16 +3695,16 @@
},
{
"name": "symfony/process",
"version": "v3.0.8",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "d7cde1f9d94d87060204f863779389b61c382eeb"
"reference": "5c11a1a4d4016662eeaf0f8757958c7de069f9a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/d7cde1f9d94d87060204f863779389b61c382eeb",
"reference": "d7cde1f9d94d87060204f863779389b61c382eeb",
"url": "https://api.github.com/repos/symfony/process/zipball/5c11a1a4d4016662eeaf0f8757958c7de069f9a0",
"reference": "5c11a1a4d4016662eeaf0f8757958c7de069f9a0",
"shasum": ""
},
"require": {
@@ -3716,7 +3713,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
},
"autoload": {
@@ -3743,20 +3740,20 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2016-06-29 05:40:00"
"time": "2016-06-29 05:42:25"
},
{
"name": "symfony/routing",
"version": "v3.0.8",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
"reference": "9038984bd9c05ab07280121e9e10f61a7231457b"
"reference": "22c7adc204057a0ff0b12eea2889782a5deb70a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/9038984bd9c05ab07280121e9e10f61a7231457b",
"reference": "9038984bd9c05ab07280121e9e10f61a7231457b",
"url": "https://api.github.com/repos/symfony/routing/zipball/22c7adc204057a0ff0b12eea2889782a5deb70a3",
"reference": "22c7adc204057a0ff0b12eea2889782a5deb70a3",
"shasum": ""
},
"require": {
@@ -3785,7 +3782,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
},
"autoload": {
@@ -3818,20 +3815,20 @@
"uri",
"url"
],
"time": "2016-06-29 05:40:00"
"time": "2016-06-29 05:41:56"
},
{
"name": "symfony/translation",
"version": "v3.0.8",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "6bf844e1ee3c820c012386c10427a5c67bbefec8"
"reference": "d63a94528530c3ea5ff46924c8001cec4a398609"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/6bf844e1ee3c820c012386c10427a5c67bbefec8",
"reference": "6bf844e1ee3c820c012386c10427a5c67bbefec8",
"url": "https://api.github.com/repos/symfony/translation/zipball/d63a94528530c3ea5ff46924c8001cec4a398609",
"reference": "d63a94528530c3ea5ff46924c8001cec4a398609",
"shasum": ""
},
"require": {
@@ -3855,7 +3852,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
},
"autoload": {
@@ -3882,20 +3879,20 @@
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
"time": "2016-06-29 05:40:00"
"time": "2016-06-29 05:41:56"
},
{
"name": "symfony/var-dumper",
"version": "v3.0.8",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "2f046e9a9d571f22cc8b26783564876713b06579"
"reference": "39492b8b8fe514163e677bf154fd80f6cc995759"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/2f046e9a9d571f22cc8b26783564876713b06579",
"reference": "2f046e9a9d571f22cc8b26783564876713b06579",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/39492b8b8fe514163e677bf154fd80f6cc995759",
"reference": "39492b8b8fe514163e677bf154fd80f6cc995759",
"shasum": ""
},
"require": {
@@ -3911,7 +3908,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
},
"autoload": {
@@ -3945,7 +3942,7 @@
"debug",
"dump"
],
"time": "2016-06-29 05:40:00"
"time": "2016-06-29 05:41:56"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@@ -4502,6 +4499,48 @@
],
"time": "2016-05-22 21:52:33"
},
{
"name": "myclabs/deep-copy",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "a8773992b362b58498eed24bf85005f363c34771"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/a8773992b362b58498eed24bf85005f363c34771",
"reference": "a8773992b362b58498eed24bf85005f363c34771",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"doctrine/collections": "1.*",
"phpunit/phpunit": "~4.1"
},
"type": "library",
"autoload": {
"psr-4": {
"DeepCopy\\": "src/DeepCopy/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Create deep copies (clones) of your objects",
"homepage": "https://github.com/myclabs/DeepCopy",
"keywords": [
"clone",
"copy",
"duplicate",
"object",
"object graph"
],
"time": "2015-11-20 12:04:31"
},
{
"name": "phpdocumentor/reflection-common",
"version": "1.0",
@@ -4712,39 +4751,40 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "2.2.4",
"version": "4.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
"reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3",
"reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"php": "^5.6 || ^7.0",
"phpunit/php-file-iterator": "~1.3",
"phpunit/php-text-template": "~1.2",
"phpunit/php-token-stream": "~1.3",
"sebastian/environment": "^1.3.2",
"sebastian/version": "~1.0"
"phpunit/php-token-stream": "^1.4.2",
"sebastian/code-unit-reverse-lookup": "~1.0",
"sebastian/environment": "^1.3.2 || ^2.0",
"sebastian/version": "~1.0|~2.0"
},
"require-dev": {
"ext-xdebug": ">=2.1.4",
"phpunit/phpunit": "~4"
"phpunit/phpunit": "^5.4"
},
"suggest": {
"ext-dom": "*",
"ext-xdebug": ">=2.2.1",
"ext-xdebug": ">=2.4.0",
"ext-xmlwriter": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2.x-dev"
"dev-master": "4.0.x-dev"
}
},
"autoload": {
@@ -4770,7 +4810,7 @@
"testing",
"xunit"
],
"time": "2015-10-06 15:47:00"
"time": "2016-07-26 14:39:29"
},
{
"name": "phpunit/php-file-iterator",
@@ -4955,16 +4995,16 @@
},
{
"name": "phpunit/phpunit",
"version": "4.8.21",
"version": "5.4.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "ea76b17bced0500a28098626b84eda12dbcf119c"
"reference": "3132365e1430c091f208e120b8845d39c25f20e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ea76b17bced0500a28098626b84eda12dbcf119c",
"reference": "ea76b17bced0500a28098626b84eda12dbcf119c",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3132365e1430c091f208e120b8845d39c25f20e6",
"reference": "3132365e1430c091f208e120b8845d39c25f20e6",
"shasum": ""
},
"require": {
@@ -4973,21 +5013,27 @@
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.3.3",
"myclabs/deep-copy": "~1.3",
"php": "^5.6 || ^7.0",
"phpspec/prophecy": "^1.3.1",
"phpunit/php-code-coverage": "~2.1",
"phpunit/php-code-coverage": "^4.0.1",
"phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": ">=1.0.6",
"phpunit/phpunit-mock-objects": "~2.3",
"phpunit/php-timer": "^1.0.6",
"phpunit/phpunit-mock-objects": "^3.2",
"sebastian/comparator": "~1.1",
"sebastian/diff": "~1.2",
"sebastian/environment": "~1.3",
"sebastian/environment": "^1.3 || ^2.0",
"sebastian/exporter": "~1.2",
"sebastian/global-state": "~1.0",
"sebastian/version": "~1.0",
"sebastian/object-enumerator": "~1.0",
"sebastian/resource-operations": "~1.0",
"sebastian/version": "~1.0|~2.0",
"symfony/yaml": "~2.1|~3.0"
},
"conflict": {
"phpdocumentor/reflection-docblock": "3.0.2"
},
"suggest": {
"phpunit/php-invoker": "~1.1"
},
@@ -4997,7 +5043,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.8.x-dev"
"dev-master": "5.4.x-dev"
}
},
"autoload": {
@@ -5023,30 +5069,33 @@
"testing",
"xunit"
],
"time": "2015-12-12 07:45:58"
"time": "2016-07-26 14:48:00"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "2.3.8",
"version": "3.2.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983"
"reference": "b13d0d9426ced06958bd32104653526a6c998a52"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983",
"reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/b13d0d9426ced06958bd32104653526a6c998a52",
"reference": "b13d0d9426ced06958bd32104653526a6c998a52",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": ">=5.3.3",
"phpunit/php-text-template": "~1.2",
"sebastian/exporter": "~1.2"
"php": "^5.6 || ^7.0",
"phpunit/php-text-template": "^1.2",
"sebastian/exporter": "^1.2"
},
"conflict": {
"phpunit/phpunit": "<5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
"phpunit/phpunit": "^5.4"
},
"suggest": {
"ext-soap": "*"
@@ -5054,7 +5103,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3.x-dev"
"dev-master": "3.2.x-dev"
}
},
"autoload": {
@@ -5079,7 +5128,52 @@
"mock",
"xunit"
],
"time": "2015-10-02 06:51:40"
"time": "2016-06-12 07:37:26"
},
{
"name": "sebastian/code-unit-reverse-lookup",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
"reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
"reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"phpunit/phpunit": "~5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"time": "2016-02-13 06:45:14"
},
{
"name": "sebastian/comparator",
@@ -5365,6 +5459,52 @@
],
"time": "2015-10-12 03:26:01"
},
{
"name": "sebastian/object-enumerator",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
"reference": "d4ca2fb70344987502567bc50081c03e6192fb26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d4ca2fb70344987502567bc50081c03e6192fb26",
"reference": "d4ca2fb70344987502567bc50081c03e6192fb26",
"shasum": ""
},
"require": {
"php": ">=5.6",
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"time": "2016-01-28 13:25:10"
},
{
"name": "sebastian/recursion-context",
"version": "1.0.2",
@@ -5419,20 +5559,70 @@
"time": "2015-11-11 19:50:13"
},
{
"name": "sebastian/version",
"version": "1.0.6",
"name": "sebastian/resource-operations",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
"reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
"url": "https://github.com/sebastianbergmann/resource-operations.git",
"reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
"reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
"url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
"reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
"shasum": ""
},
"require": {
"php": ">=5.6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"time": "2015-07-28 20:34:47"
},
{
"name": "sebastian/version",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
"reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5",
"reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
@@ -5451,7 +5641,7 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2015-06-21 13:59:46"
"time": "2016-02-04 12:56:52"
},
{
"name": "symfony/dom-crawler",
@@ -5614,10 +5804,10 @@
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
"php": ">=5.5.9"
"php": ">=5.6.4"
},
"platform-dev": [],
"platform-overrides": {
"php": "5.5.9"
"php": "5.6.4"
}
}

View File

@@ -208,6 +208,7 @@ return [
'Artisan' => 'Illuminate\Support\Facades\Artisan',
'Auth' => 'Illuminate\Support\Facades\Auth',
'Blade' => 'Illuminate\Support\Facades\Blade',
'Broadcast' => 'Illuminate\Support\Facades\Broadcast',
'Cache' => 'Illuminate\Support\Facades\Cache',
'Config' => 'Illuminate\Support\Facades\Config',
'Cookie' => 'Illuminate\Support\Facades\Cookie',

View File

@@ -57,7 +57,15 @@ return [
],
'memcached' => [
'driver' => 'memcached',
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
],
'servers' => [
[
'host' => '127.0.0.1',

View File

@@ -71,7 +71,7 @@ return [
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => env('DB_PREFIX', null),
'strict' => false,
'strict' => true,
'engine' => null,
],
@@ -85,6 +85,7 @@ return [
'charset' => 'utf8',
'prefix' => env('DB_PREFIX', null),
'schema' => env('DB_SCHEMA', 'public'),
'sslmode' => 'prefer',
],
'sqlsrv' => [

View File

@@ -94,6 +94,19 @@ return [
'table' => 'sessions',
/*
|--------------------------------------------------------------------------
| Session Cache Store
|--------------------------------------------------------------------------
|
| When using the "apc" or "memcached" session drivers, you may specify a
| cache store that should be used for these sessions. This value must
| correspond with one of the application's configured cache stores.
|
*/
'store' => null,
/*
|--------------------------------------------------------------------------
| Session Sweeping Lottery

View File

@@ -11,14 +11,18 @@
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use CachetHQ\Cachet\Models\ComponentTag;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\IncidentUpdate;
use CachetHQ\Cachet\Models\Metric;
use CachetHQ\Cachet\Models\MetricPoint;
use CachetHQ\Cachet\Models\Subscriber;
use CachetHQ\Cachet\Models\Subscription;
use CachetHQ\Cachet\Models\Tag;
use CachetHQ\Cachet\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Str;
$factory->define(Component::class, function ($faker) {
return [
@@ -38,6 +42,13 @@ $factory->define(ComponentGroup::class, function ($faker) {
];
});
$factory->define(ComponentTag::class, function ($faker) {
return [
'component_id' => factory(Component::class)->create()->id,
'tag_id' => factory(Tag::class)->create()->id,
];
});
$factory->define(Incident::class, function ($faker) {
return [
'name' => $faker->sentence(),
@@ -55,6 +66,15 @@ $factory->define(IncidentTemplate::class, function ($faker) {
];
});
$factory->define(IncidentUpdate::class, function ($faker) {
return [
'incident_id' => factory(Incident::class)->create()->id,
'message' => $faker->paragraph(),
'status' => random_int(1, 4),
'user_id' => factory(User::class)->create()->id,
];
});
$factory->define(Metric::class, function ($faker) {
return [
'name' => $faker->sentence(),
@@ -91,6 +111,15 @@ $factory->define(Subscription::class, function ($faker) {
];
});
$factory->define(Tag::class, function ($faker) {
$name = $faker->sentence();
return [
'name' => $name,
'slug' => Str::slug($name),
];
});
$factory->define(User::class, function ($faker) {
return [
'username' => $faker->userName,

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateIncidentUpdatesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('incident_updates', function (Blueprint $table) {
$table->increments('id');
$table->integer('incident_id')->unsigned();
$table->integer('status');
$table->longText('message');
$table->integer('user_id')->unsigned();
$table->timestamps();
$table->index('incident_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('incident_updates');
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
class RenameComponentTagTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::rename('component_tag', 'component_tags');
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::rename('component_tags', 'component_tag');
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AlterTableComponentTagsAddTimestamps extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('component_tags', function (Blueprint $table) {
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('component_tags', function (Blueprint $table) {
$table->dropTimestamps();
});
}
}

View File

@@ -276,6 +276,14 @@ body.status-page {
line-height: 1.3em;
}
i.icon {
font-size: 21px;
line-height: 24px;
text-align: center;
display: inline-block;
min-width: 20px;
}
&.group-name {
background-color: $cachet_gray_light;
padding: {

View File

@@ -29,6 +29,7 @@ body {
@import "modules/panels";
@import "modules/btns";
@import "modules/pager";
@import "modules/markdown";
// Styles for partials
@import "partials/base";

View File

@@ -0,0 +1,37 @@
.markdown-body {
p {
font-size: 1.1em !important;
}
h1, h2, h3, h4, h5, h6 {
font-weight: normal;
}
h1 {
font-size: 30px;
}
h2 {
font-size: 28px;
}
h3 {
font-size: 24px;
}
h4 {
font-size: 22px;
}
h5 {
font-size: 18px;
}
h6 {
font-size: 16px;
}
a {
color: $cachet-base-dark;
}
}

View File

@@ -20,6 +20,7 @@ return [
'logged' => '{0} There are no incidents, good work.|You have logged one incident.|You have reported <strong>:count</strong> incidents.',
'incident-create-template' => 'Create Template',
'incident-templates' => 'Incident Templates',
'updates' => '{0} Zero Updates|One Update|:count Updates',
'add' => [
'title' => 'Report an incident',
'success' => 'Incident added.',
@@ -34,6 +35,10 @@ return [
'success' => 'The incident has been deleted and will not show on your status page.',
'failure' => 'The incident could not be deleted, please try again.',
],
'update' => [
'title' => 'Create new incident update',
'subtitle' => 'Add an update to <strong>:incident</strong>',
],
// Incident templates
'templates' => [

View File

@@ -24,13 +24,14 @@
@foreach($incidents as $incident)
<div class="row striped-list-item">
<div class="col-xs-6">
<i class="{{ $incident->icon }}"></i> <strong>{{ $incident->name }}</strong>
<i class="{{ $incident->icon }}"></i> <strong>{{ $incident->name }}</strong> <span class="badge badge-info">{{ trans_choice('dashboard.incidents.updates', $incident->updates->count()) }}</span>
@if($incident->message)
<p><small>{{ Str::words($incident->message, 5) }}</small></p>
@endif
</div>
<div class="col-xs-6 text-right">
<a href="/dashboard/incidents/{{ $incident->id }}/edit" class="btn btn-default">{{ trans('forms.edit') }}</a>
<a href="/dashboard/incidents/{{ $incident->id }}/update" class="btn btn-info">{{ trans('forms.update') }}</a>
<a href="/dashboard/incidents/{{ $incident->id }}/delete" class="btn btn-danger confirm-action" data-method='DELETE'>{{ trans('forms.delete') }}</a>
</div>
</div>

View File

@@ -0,0 +1,64 @@
@extends('layout.dashboard')
@section('content')
<div class="header">
<div class="sidebar-toggler visible-xs">
<i class="icon ion-navicon"></i>
</div>
<span class="uppercase">
<i class="icon ion-android-alert"></i> {{ trans('dashboard.incidents.incidents') }}
</span>
&gt; <small>{{ trans('dashboard.incidents.update.title') }}</small>
</div>
<div class="content-wrapper">
<div class="row">
<div class="col-md-12">
@include('dashboard.partials.errors')
<p class="lead">{!! trans('dashboard.incidents.update.subtitle', ['incident' => $incident->name]) !!}</p>
<form class="form-vertical" name="IncidentUpdateForm" role="form" method="POST" autocomplete="off">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<fieldset>
<div class="form-group">
<label for="incident-name">{{ trans('forms.incidents.status') }}</label><br>
<label class="radio-inline">
<input type="radio" name="status" value="1">
<i class="icon ion-flag"></i>
{{ trans('cachet.incidents.status')[1] }}
</label>
<label class="radio-inline">
<input type="radio" name="status" value="2">
<i class="icon ion-alert-circled"></i>
{{ trans('cachet.incidents.status')[2] }}
</label>
<label class="radio-inline">
<input type="radio" name="status" value="3">
<i class="icon ion-eye"></i>
{{ trans('cachet.incidents.status')[3] }}
</label>
<label class="radio-inline">
<input type="radio" name="status" value="4">
<i class="icon ion-checkmark"></i>
{{ trans('cachet.incidents.status')[4] }}
</label>
</div>
<div class="form-group">
<label>{{ trans('forms.incidents.message') }}</label>
<div class="markdown-control">
<textarea name="message" class="form-control autosize" rows="5" required></textarea>
</div>
</div>
</fieldset>
<input type="hidden" name="incident_id" value={{ $incident->id }}>
<div class="form-group">
<div class="btn-group">
<button type="submit" class="btn btn-success">{{ trans('forms.update') }}</button>
<a class="btn btn-default" href="{{ route('dashboard.incidents.index') }}">{{ trans('forms.cancel') }}</a>
</div>
</div>
</form>
</div>
</div>
</div>
@stop

View File

@@ -22,4 +22,11 @@
<div class="panel-body markdown-body">
{!! $incident->formattedMessage !!}
</div>
@if($incident->updates->count())
<div class="list-group">
@foreach($incident->updates as $update)
<div class="list-group-item"><i class="{{ $update->icon }}" title="{{ $update->human_status }}" data-toggle="tooltip"></i> <strong>{{ Str::limit($update->raw_message, 20) }}</strong> <small><a href="{{ $incident->permalink }}#update-{{ $update->id }}">{{ $update->created_at_diff }}</a></small></div>
@endforeach
</div>
@endif
</div>

View File

@@ -5,8 +5,8 @@
<div class="moment {{ $incidentID === 0 ? 'first' : null }}">
<div class="row event clearfix">
<div class="col-sm-1">
<div class="status-icon status-{{ $incident->status }}" data-toggle="tooltip" title="{{ $incident->human_status }}" data-placement="left">
<i class="{{ $incident->icon }}"></i>
<div class="status-icon status-{{ $incident->latest_human_status }}" data-toggle="tooltip" title="{{ $incident->latest_human_status }}" data-placement="left">
<i class="{{ $incident->latest_icon }}"></i>
</div>
</div>
<div class="col-xs-10 col-xs-offset-2 col-sm-11 col-sm-offset-0">

View File

@@ -7,22 +7,41 @@
@stop
@section('content')
<h1>{{ formatted_date($incident->created_at) }}</h1>
<h1>{{ $incident->name }} <small>{{ formatted_date($incident->created_at) }}</small></h1>
<hr>
<div class="markdown-body">
{!! $incident->formattedMessage !!}
</div>
@if($incident->updates)
<div class="timeline">
<div class="content-wrapper">
<div class="moment first">
@foreach ($incident->updates as $index => $update)
<div class="moment {{ $index === 0 ? 'first' : null }}" id="update-{{ $update->id }}">
<div class="row event clearfix">
<div class="col-sm-1">
<div class="status-icon status-{{ $incident->status }}" data-toggle="tooltip" title="{{ $incident->human_status }}" data-placement="left">
<i class="{{ $incident->icon }}"></i>
<div class="status-icon status-{{ $update->status }}" data-toggle="tooltip" title="{{ $update->human_status }}" data-placement="left">
<i class="{{ $update->icon }}"></i>
</div>
</div>
<div class="col-xs-10 col-xs-offset-2 col-sm-11 col-sm-offset-0">
@include('partials.incident', ['incident' => $incident, 'with_link' => false])
<div class="panel panel-message incident">
<div class="panel-body">
<div class="markdown-body">
{!! $update->formattedMessage !!}
</div>
</div>
<div class="panel-footer">
<p>Updated {{ $update->created_at_diff }}</p>
</div>
</div>
</div>
</div>
</div>
@endforeach
</div>
</div>
@endif
@stop

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Api;
/**
* This is the component tag test class.
*
* @author James Brooks <james@alt-three.com>
*/
class ComponentTagTest extends AbstractApiTestCase
{
public function testGetTags()
{
$tags = factory('CachetHQ\Cachet\Models\ComponentTag', 3)->create();
$this->get('/api/v1/components/tags');
$this->seeJson(['id' => $tags[0]->id]);
$this->seeJson(['id' => $tags[1]->id]);
$this->seeJson(['id' => $tags[2]->id]);
$this->assertResponseOk();
}
public function testGetInvalidTag()
{
$this->get('/api/v1/components/tags/1');
$this->assertResponseStatus(404);
}
public function testPostTagUnauthorized()
{
$this->post('/api/v1/components/tags');
$this->assertResponseStatus(401);
}
public function testPostTagNoData()
{
$this->beUser();
$this->post('/api/v1/components/tags');
$this->assertResponseStatus(400);
}
public function testPostTag()
{
$this->beUser();
$component = factory('CachetHQ\Cachet\Models\Component')->create();
$tag = factory('CachetHQ\Cachet\Models\Tag')->create();
$this->post('/api/v1/components/tags', [
'component_id' => $component->id,
'tag_id' => $tag->id,
]);
$this->seeJson(['component_id' => $component->id, 'tag_id' => $tag->id]);
$this->assertResponseOk();
}
public function testGetNewTag()
{
$tag = factory('CachetHQ\Cachet\Models\ComponentTag')->create();
$this->get('/api/v1/components/tags/1');
$this->seeJson(['tag_id' => $tag->id]);
$this->assertResponseOk();
}
public function testDeleteTag()
{
$this->beUser();
$tag = factory('CachetHQ\Cachet\Models\ComponentTag')->create();
$this->delete('/api/v1/components/tags', [
'component_id' => $tag->component_id,
'tag_id' => $tag->tag_id,
]);
$this->assertResponseStatus(204);
}
}

View File

@@ -0,0 +1,102 @@
<?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\Tests\Cachet\Api;
/**
* This is the incident update test class.
*
* @author James Brooks <james@alt-three.com>
*/
class IncidentUpdateTest extends AbstractApiTestCase
{
public function testGetIncidentUpdates()
{
$incident = factory('CachetHQ\Cachet\Models\Incident')->create();
$updates = factory('CachetHQ\Cachet\Models\IncidentUpdate', 3)->create([
'incident_id' => $incident->id,
]);
$this->get("/api/v1/incidents/{$incident->id}/updates");
$this->assertResponseOk();
$this->seeJson(['id' => $updates[0]->id]);
$this->seeJson(['id' => $updates[1]->id]);
$this->seeJson(['id' => $updates[2]->id]);
}
public function testGetInvalidIncidentUpdate()
{
$this->get('/api/v1/incidents/1/updates/1');
$this->assertResponseStatus(404);
}
public function testPostIncidentUpdateUnauthorized()
{
$incident = factory('CachetHQ\Cachet\Models\Incident')->create();
$this->post("/api/v1/incidents/{$incident->id}/updates");
$this->assertResponseStatus(401);
}
public function testPostIncidentUpdateNoData()
{
$this->beUser();
$incident = factory('CachetHQ\Cachet\Models\Incident')->create();
$this->post("/api/v1/incidents/{$incident->id}/updates");
$this->assertResponseStatus(400);
}
public function testPostIncidentUpdate()
{
$this->beUser();
$incident = factory('CachetHQ\Cachet\Models\Incident')->create();
$this->post("/api/v1/incidents/{$incident->id}/updates", [
'status' => 4,
'message' => 'Incident fixed!',
]);
$this->assertResponseOk();
$this->seeJson(['incident_id' => $incident->id]);
}
public function testPutIncidentUpdate()
{
$this->beUser();
$incident = factory('CachetHQ\Cachet\Models\Incident')->create();
$update = factory('CachetHQ\Cachet\Models\IncidentUpdate')->create();
$this->put("/api/v1/incidents/{$incident->id}/updates/{$update->id}", [
'message' => 'Message updated :smile:',
]);
$this->assertResponseOk();
$this->seeJson(['message' => 'Message updated :smile:']);
}
public function testDeleteIncidentUpdate()
{
$this->beUser();
$incident = factory('CachetHQ\Cachet\Models\Incident')->create();
$update = factory('CachetHQ\Cachet\Models\IncidentUpdate')->create();
$this->delete("/api/v1/incidents/{$incident->id}/updates/{$update->id}");
$this->assertResponseStatus(204);
}
}

94
tests/Api/TagTest.php Normal file
View File

@@ -0,0 +1,94 @@
<?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\Tests\Cachet\Api;
/**
* This is the tag test class.
*
* @author James Brooks <james@alt-three.com>
*/
class TagTest extends AbstractApiTestCase
{
public function testGetTags()
{
$tags = factory('CachetHQ\Cachet\Models\Tag', 3)->create();
$this->get('/api/v1/tags');
$this->seeJson(['id' => $tags[0]->id]);
$this->seeJson(['id' => $tags[1]->id]);
$this->seeJson(['id' => $tags[2]->id]);
$this->assertResponseOk();
}
public function testGetInvalidTag()
{
$this->get('/api/v1/tags/1');
$this->assertResponseStatus(404);
}
public function testPostTagUnauthorized()
{
$this->post('/api/v1/tags');
$this->assertResponseStatus(401);
}
public function testPostTagNoData()
{
$this->beUser();
$this->post('/api/v1/tags');
$this->assertResponseStatus(400);
}
public function testPostTag()
{
$this->beUser();
$this->post('/api/v1/tags', [
'name' => 'Foo',
'slug' => 'foo',
]);
$this->seeJson(['name' => 'Foo', 'slug' => 'foo']);
$this->assertResponseOk();
}
public function testGetNewTag()
{
$tag = factory('CachetHQ\Cachet\Models\Tag')->create();
$this->get('/api/v1/tags/1');
$this->seeJson(['name' => $tag->name]);
$this->assertResponseOk();
}
public function testPutTag()
{
$this->beUser();
$tag = factory('CachetHQ\Cachet\Models\Tag')->create();
$this->put('/api/v1/tags/1', [
'name' => 'Lorem Ipsum Tagous',
]);
$this->seeJson(['name' => 'Lorem Ipsum Tagous']);
$this->assertResponseOk();
}
public function testDeleteTag()
{
$this->beUser();
$tag = factory('CachetHQ\Cachet\Models\Tag')->create();
$this->delete('/api/v1/tags/1');
$this->assertResponseStatus(204);
}
}

57
tests/Api/UserTest.php Normal file
View File

@@ -0,0 +1,57 @@
<?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\Tests\Cachet\Api;
/**
* This is the user test class.
*
* @author James Brooks <james@alt-three.com>
*/
class UserTest extends AbstractApiTestCase
{
public function testCreateUser()
{
$this->beUser();
$this->post('/api/v1/users', [
'username' => 'Alt Three',
'email' => 'support@alt-three.com',
'password' => 'AltTheeWinsLife',
]);
$this->assertResponseOk();
$this->seeHeader('Content-Type', 'application/json');
$this->seeJson(['email' => 'support@alt-three.com']);
}
public function testUpdateUser()
{
$this->beUser();
$user = factory('CachetHQ\Cachet\Models\User')->create();
$this->put("/api/v1/users/{$user->id}", [
'username' => 'jbrooksuk',
]);
$this->seeHeader('Content-Type', 'application/json');
$this->seeJson(['username' => 'jbrooksuk']);
}
public function testDeleteUser()
{
$this->beUser();
$user = factory('CachetHQ\Cachet\Models\User')->create();
$this->delete("/api/v1/users/{$user->id}");
$this->assertResponseStatus(204);
}
}

View File

@@ -0,0 +1,48 @@
<?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\Tests\Cachet\Bus\Commands\ComponentTag;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\ComponentTag\AddComponentTagCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\ComponentTag\AddComponentTagCommandHandler;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Tag;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the add component tag command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class AddComponentTagCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = ['component' => new Component(), 'tag' => new Tag()];
$object = new AddComponentTagCommand($params['component'], $params['tag']);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return AddComponentTagCommandHandler::class;
}
}

View File

@@ -0,0 +1,48 @@
<?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\Tests\Cachet\Bus\Commands\ComponentTag;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\ComponentTag\DeleteComponentTagCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\ComponentTag\DeleteComponentTagCommandHandler;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Tag;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the delete component tag command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class DeleteComponentTagCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = ['component' => new Component(), 'tag' => new Tag()];
$object = new DeleteComponentTagCommand($params['component'], $params['tag']);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return DeleteComponentTagCommandHandler::class;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Commands\IncidentUpdate;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\RemoveIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\IncidentUpdate\RemoveIncidentUpdateCommandHandler;
use CachetHQ\Cachet\Models\IncidentUpdate;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the remove incident update command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class RemoveIncidentUpdateCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = ['incidentUpdate' => new IncidentUpdate()];
$object = new RemoveIncidentUpdateCommand($params['incidentUpdate']);
return compact('params', 'object');
}
protected function getHandlerClass()
{
return RemoveIncidentUpdateCommandHandler::class;
}
}

View File

@@ -0,0 +1,52 @@
<?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\Tests\Cachet\Bus\Commands\IncidentUpdate;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\ReportIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\IncidentUpdate\ReportIncidentUpdateCommandHandler;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\User;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the report incident update command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class ReportIncidentUpdateCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = [
'incident' => new Incident(),
'status' => 1,
'message' => 'Foo',
'user' => new User(),
];
$object = new ReportIncidentUpdateCommand($params['incident'], $params['status'], $params['message'], $params['user']);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return ReportIncidentUpdateCommandHandler::class;
}
}

View File

@@ -0,0 +1,52 @@
<?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\Tests\Cachet\Bus\Commands\IncidentUpdate;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\UpdateIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\IncidentUpdate\UpdateIncidentUpdateCommandHandler;
use CachetHQ\Cachet\Models\IncidentUpdate;
use CachetHQ\Cachet\Models\User;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the update incident update command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class UpdateIncidentUpdateCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = ['update' => new IncidentUpdate(), 'status' => 1, 'message' => 'Updating!', 'user' => new User()];
$object = new UpdateIncidentUpdateCommand(
$params['update'],
$params['status'],
$params['message'],
$params['user']
);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return UpdateIncidentUpdateCommandHandler::class;
}
}

View File

@@ -0,0 +1,45 @@
<?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\Tests\Cachet\Bus\Commands\Tag;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\Tag\CreateTagCommandHandler;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the create tag command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class CreateTagCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = ['name' => 'Hello World', 'slug' => 'hello-world'];
$object = new CreateTagCommand($params['name'], $params['slug']);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return CreateTagCommandHandler::class;
}
}

View File

@@ -0,0 +1,46 @@
<?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\Tests\Cachet\Bus\Commands\Tag;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\Tag\DeleteTagCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\Tag\DeleteTagCommandHandler;
use CachetHQ\Cachet\Models\Tag;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the delete tag command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class DeleteTagCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = ['tag' => new Tag()];
$object = new DeleteTagCommand($params['tag']);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return DeleteTagCommandHandler::class;
}
}

View File

@@ -0,0 +1,46 @@
<?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\Tests\Cachet\Bus\Commands\Tag;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\Tag\UpdateTagCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\Tag\UpdateTagCommandHandler;
use CachetHQ\Cachet\Models\Tag;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the update tag command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class UpdateTagCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = ['tag' => new Tag(), 'name' => 'Hello World', 'slug' => 'hello-world'];
$object = new UpdateTagCommand($params['tag'], $params['name'], $params['slug']);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return UpdateTagCommandHandler::class;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Bus\Commands\User;
use AltThree\TestBench\CommandTrait;
use CachetHQ\Cachet\Bus\Commands\User\UpdateTeamMemberCommand;
use CachetHQ\Cachet\Bus\Handlers\Commands\User\UpdateTeamMemberCommandHandler;
use CachetHQ\Cachet\Models\User;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the update team member command test class.
*
* @author James Brooks <james@alt-three.com>
*/
class UpdateTeamMemberCommandTest extends AbstractTestCase
{
use CommandTrait;
protected function getObjectAndParams()
{
$params = [
'user' => new User(),
'username' => 'Test',
'password' => 'fooey',
'email' => 'test@test.com',
'level' => 1,
];
$object = new UpdateTeamMemberCommand(
$params['user'],
$params['username'],
$params['password'],
$params['email'],
$params['level']
);
return compact('params', 'object');
}
protected function objectHasRules()
{
return true;
}
protected function getHandlerClass()
{
return UpdateTeamMemberCommandHandler::class;
}
}

View File

@@ -0,0 +1,31 @@
<?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\Tests\Cachet\Bus\Events\IncidentUpdate;
use AltThree\TestBench\EventTrait;
use CachetHQ\Cachet\Bus\Events\IncidentUpdate\IncidentUpdateEventInterface;
use CachetHQ\Tests\Cachet\AbstractTestCase;
/**
* This is the abstract incident update event test case class.
*
* @author James Brooks <james@alt-three.com>
*/
abstract class AbstractIncidentUpdateEventTestCase extends AbstractTestCase
{
use EventTrait;
protected function getEventInterfaces()
{
return [IncidentUpdateEventInterface::class];
}
}