<?php

namespace MrJuliuss\Syntara\Controllers;

use MrJuliuss\Syntara\Controllers\BaseController;
use MrJuliuss\Syntara\Services\Validators\Group as GroupValidator;
use PermissionProvider;
use View;
use Input;
use Config;
use Response;
use Sentry;
use Request;
use DB;
use URL;

class GroupController extends BaseController
{
    /**
    * List of groups
    */
    public function getIndex()
    {
        $emptyGroup =  Sentry::getGroupProvider()->createModel();

        // Ajax search
        $groupId = Input::get('groupIdSearch');
        if(!empty($groupId)) {
            $emptyGroup = $emptyGroup->where('id', $groupId);
        }
        $groupname = Input::get('groupnameSearch');
        if(!empty($groupname)) {
            $emptyGroup = $emptyGroup->where('name', 'LIKE', '%'.$groupname.'%');
        }

        $groups = $emptyGroup->paginate(Config::get('syntara::config.item-perge-page'));

        // ajax: reload only the content container
        if(Request::ajax()) {
            $html = View::make(Config::get('syntara::views.groups-list'), array('groups' => $groups))->render();

            return Response::json(array('html' => $html));
        }

        $this->layout = View::make(Config::get('syntara::views.groups-index'), array('groups' => $groups));
        $this->layout->title = trans('syntara::groups.titles.list');
        $this->layout->breadcrumb = Config::get('syntara::breadcrumbs.groups');
    }

    /**
    * Show create group view
    */
    public function getCreate()
    {
        $permissions = PermissionProvider::findAll();

        $this->layout = View::make(Config::get('syntara::views.group-create'), array('permissions' => $permissions));
        $this->layout->title = trans('syntara::groups.titles.new');
        $this->layout->breadcrumb = Config::get('syntara::breadcrumbs.create_group');
    }

    /**
    * Create group
    */
    public function postCreate()
    {
        $groupname = Input::get('groupname');
        $permissions = array();

        $errors = $this->_validateGroup(Input::get('permission'), $groupname, $permissions);
        if(!empty($errors)) {
            return Response::json(array('groupCreated' => false, 'errorMessages' => $errors));
        } else {
            try {
                // create group
                Sentry::getGroupProvider()->create(array(
                    'name' => $groupname,
                    'permissions' => $permissions,
                ));
            } catch (\Cartalyst\Sentry\Groups\NameRequiredException $e) {} catch (\Cartalyst\Sentry\Groups\GroupExistsException $e) {
                return Response::json(array('groupCreated' => false, 'message' => trans('syntara::groups.messages.exists'), 'messageType' => 'danger'));
            }
        }

        return Response::json(array('groupCreated' => true,  'redirectUrl' => URL::route('listGroups')));
    }

    /**
     * Show group
     * @param type $groupId
     */
    public function getShow($groupId)
    {
        try {
            $group = Sentry::getGroupProvider()->findById($groupId);
            $permissions = PermissionProvider::findAll();

            $groupPermissions = array();
            foreach($group->getPermissions() as $permissionValue => $key) {
                try {
                    $p = PermissionProvider::findByValue($permissionValue);
                    foreach($permissions as $key => $permission) {
                        if($p->getId() === $permission->getId()) {
                            $groupPermissions[] = $permission;
                            unset($permissions[$key]);
                        }
                    }
                } catch(\MrJuliuss\Syntara\Models\Permissions\PermissionNotFoundException $e){}
            }

            // get users in group
            $users = $group->users()->paginate(Config::get('syntara::config.item-perge-page'));

            // users not in group
            $usersNotInGroup = Sentry::getUserProvider()
                ->createModel()
                ->newQuery()
                ->whereNotIn('id', function ($query) use ($groupId) {
                    $query->select('user_id')
                          ->from('users_groups')
                          ->where('group_id', '=', $groupId);
                })
                ->get()
                ->all();

            // ajax request : reload only content container
            if(Request::ajax()) {
                $html = View::make(Config::get('syntara::views.users-in-group'), array('group' => $group, 'users' => $users, 'candidateUsers' => $usersNotInGroup))->render();

                return Response::json(array('html' => $html));
            }

            $this->layout = View::make(Config::get('syntara::views.group-edit'), array('group' => $group, 'users' => $users, 'candidateUsers' => $usersNotInGroup, 'permissions' => $permissions, 'ownPermissions' => $groupPermissions));
            $this->layout->title = 'Group '.$group->getName();
            $this->layout->breadcrumb = array(
                array(
                    'title' => trans('syntara::breadcrumbs.groups'),
                    'link' => URL::route('listGroups'),
                    'icon' => 'glyphicon-list-alt'
                ),
                array(
                    'title' => $group->name,
                    'link' => URL::current(),
                    'icon' => ''
                )
            );
        } catch (\Cartalyst\Sentry\Groups\GroupNotFoundException $e) {
            $this->layout = View::make(Config::get('syntara::views.error'), array('message' => trans('syntara::groups.messages.not-found')));
        }
    }

    /**
     * Edit group action
     * @param int $groupId
     */
    public function putShow($groupId)
    {
        $groupname = Input::get('groupname');
        $permissions = array();

        $errors = $this->_validateGroup(Input::get('permission'), $groupname, $permissions);
        if(!empty($errors)) {
            return Response::json(array('groupUpdated' => false, 'errorMessages' => $errors));
        } else {
            try {
                $group = Sentry::getGroupProvider()->findById($groupId);
                $group->name = $groupname;
                $group->permissions = $permissions;

                $permissions = (empty($permissions)) ? '' : json_encode($permissions);
                // delete permissions in db
                DB::table('groups')
                    ->where('id', $groupId)
                    ->update(array('permissions' => $permissions));

                if($group->save()) {
                    return Response::json(array('groupUpdated' => true, 'message' => trans('syntara::groups.messages.success'), 'messageType' => 'success'));
                } else {
                    return Response::json(array('groupUpdated' => false, 'message' => trans('syntara::groups.messages.try'), 'messageType' => 'danger'));
                }
            } catch (\Cartalyst\Sentry\Groups\NameRequiredException $e) {} catch (\Cartalyst\Sentry\Groups\GroupExistsException $e) {
                return Response::json(array('groupUpdated' => false, 'message' => trans('syntara::groups.messages.exists'), 'messageType' => 'danger'));
            }
        }
    }

    /**
     * Delete group
     * @param  int $groupId
     * @return Response
     */
    public function delete($groupId)
    {
        try {
            $group = Sentry::getGroupProvider()->findById($groupId);
            $group->delete();
        } catch (\Cartalyst\Sentry\Groups\GroupNotFoundException $e) {
            return Response::json(array('deletedGroup' => false, 'message' => trans('syntara::groups.messages.not-found'), 'messageType' => 'danger'));
        }

        return Response::json(array('deletedGroup' => true, 'message' => trans('syntara::groups.messages.delete-success'), 'messageType' => 'success'));
    }

    /**
     * Remove user from group
     * @param int $groupId
     * @param int $userId
     * @return Response
     */
    public function deleteUserFromGroup($groupId, $userId)
    {
        try {
            $user = Sentry::getUserProvider()->findById($userId);
            $group = Sentry::getGroupProvider()->findById($groupId);
            $user->removeGroup($group);

            return Response::json(array('userDeleted' => true, 'message' => trans('syntara::groups.messages.user-removed-success'), 'messageType' => 'success'));
        } catch (\Cartalyst\Sentry\Users\UserNotFoundException $e) {
            return Response::json(array('userDeleted' => false, 'message' => trans('syntara::users.messages.not-found'), 'messageType' => 'danger'));
        } catch(\Cartalyst\Sentry\Groups\GroupNotFoundException $e) {
            return Response::json(array('userDeleted' => false, 'message' => trans('syntara::groups.messages.not-found'), 'messageType' => 'danger'));
        }
    }

    /**
     * Add a user in a group
     * @return Response
     */
    public function addUserInGroup()
    {
        try {
            $user = Sentry::getUserProvider()->findById(Input::get('userId'));
            $group = Sentry::getGroupProvider()->findById(Input::get('groupId'));
            $user->addGroup($group);

            return Response::json(array('userAdded' => true, 'message' => trans('syntara::groups.messages.user-add-success'), 'messageType' => 'success'));
        } catch (\Cartalyst\Sentry\Users\UserNotFoundException $e) {
            return Response::json(array('userAdded' => false, 'message' => trans('syntara::users.messages.not-found'), 'messageType' => 'danger'));
        } catch(\Cartalyst\Sentry\Groups\GroupNotFoundException $e) {
            return Response::json(array('userAdded' => false, 'message' => trans('syntara::groups.messages.not-found'), 'messageType' => 'danger'));
        }
    }

    /**
     * Validate group informations
     * @param array $permissionsValues
     * @param string $groupname
     * @return array
     */
    protected function _validateGroup($permissionsValues, $groupname, &$permissions)
    {
        $errors = array();
        // validate permissions
        if(!empty($permissionsValues)) {
            foreach($permissionsValues as $key => $permission) {
               $permissions[$key] = 1;
            }
        }
        // validate group name
        $validator = new GroupValidator(Input::all());

        $gnErrors = array();
        if(!$validator->passes()) {
            $gnErrors = $validator->getErrors();
        }

        return $gnErrors;
    }
}


<?php

namespace MrJuliuss\Syntara;

use Illuminate\Support\ServiceProvider;
use Illuminate\View\Environment;

class SyntaraServiceProvider extends ServiceProvider
{

    /**
    * Indicates if loading of the provider is deferred.
    *
    * @var bool
    */
    protected $defer = false;

    public function boot()
    {
        $this->package('mrjuliuss/syntara');
        $this->loadIncludes();
    }

    /**
    * Register the service provider.
    *
    * @return void
    */
    public function register()
    {
        // load package config
        $this->app['config']->package('mrjuliuss/syntara', __DIR__.'/../../config');

        // add the user seed command to the application
        $this->app['create:user'] = $this->app->share(function ($app) {
            return new Commands\UserSeedCommand($app);
        });

        // add the install command to the application
        $this->app['syntara:install'] = $this->app->share(function ($app) {
            return new Commands\InstallCommand($app);
        });

        // add the update command to the application
        $this->app['syntara:update'] = $this->app->share(function ($app) {
            return new Commands\UpdateCommand($app);
        });

        // register helpers
        $this->registerHelpers();

        // register models
        $this->registerModels();

        // add commands
        $this->commands('create:user');
        $this->commands('syntara:install');
        $this->commands('syntara:update');
    }

    /**
    * Get the services provided by the provider.
    *
    * @return array
    */
    public function provides()
    {
        return array();
    }

    /**
     * Include some specific files from the src-root.
     *
     * @return void
     */
    private function loadIncludes()
    {
        // Add file names without the `php` extension to this list as needed.
        $filesToLoad = array(
            'composers',
            'filters',
            'routes',
        );

        // Run through $filesToLoad array.
        foreach ($filesToLoad as $file) {
            // Add needed database structure and file extension.
            $file = __DIR__ . '/../../' . $file . '.php';
            // If file exists, include.
            if (is_file($file)) include $file;
        }
    }

    /**
    * Register helpers in app
    */
    public function registerHelpers()
    {
        // register breadcrumbs
        $this->app['breadcrumbs'] = $this->app->share(function () {
            return new \MrJuliuss\Syntara\Helpers\Breadcrumbs();
        });

        // shortcut so developers don't need to add an Alias in app/config/app.php
        $this->app->booting(function () {
            $loader = \Illuminate\Foundation\AliasLoader::getInstance();
            $loader->alias('Breadcrumbs', 'MrJuliuss\Syntara\Facades\Breadcrumbs');
        });
    }

    public function registerModels()
    {
        // register permission provider
        $this->app['permissionProvider'] = $this->app->share(function () {
            return new \MrJuliuss\Syntara\Models\Permissions\PermissionProvider();
        });

        // add permission provider to aliases
        $this->app->booting(function () {
            $loader = \Illuminate\Foundation\AliasLoader::getInstance();
            $loader->alias('PermissionProvider', 'MrJuliuss\Syntara\Facades\PermissionProvider');
        });
    }

}


<?php

namespace MrJuliuss\Syntara\Controllers;

use MrJuliuss\Syntara\Controllers\BaseController;
use MrJuliuss\Syntara\Services\Validators\User as UserValidator;
use View;
use Input;
use Sentry;
use Redirect;
use Config;
use Response;

class DashboardController extends BaseController
{
    /**
    * Index loggued page
    */
    public function getIndex()
    {
        $this->layout = View::make(Config::get('syntara::views.dashboard-index'));
        $this->layout->title = trans('syntara::all.titles.index');
        $this->layout->breadcrumb = Config::get('syntara::breadcrumbs.dashboard');
    }

    /**
    * Login page
    */
    public function getLogin()
    {
        $loginAttribute = Config::get('cartalyst/sentry::users.login_attribute');
        $this->layout = View::make(Config::get('syntara::views.login'), array('loginAttribute' => $loginAttribute));
        $this->layout->title = trans('syntara::all.titles.login');
        $this->layout->breadcrumb = Config::get('syntara::breadcrumbs.login');
    }

    /**
    * Login post authentication
    */
    public function postLogin()
    {
        try {
            $validator = new UserValidator(Input::all(), 'login');
            $loginAttribute = Config::get('cartalyst/sentry::users.login_attribute');

            if(!$validator->passes()) {
                 return Response::json(array('logged' => false, 'errorMessages' => $validator->getErrors()));
            }

            $credentials = array(
                $loginAttribute => Input::get($loginAttribute),
                'password' => Input::get('pass'),
            );

            // authenticate user
            Sentry::authenticate($credentials, (bool)Input::get('remember'));
        } catch(\Cartalyst\Sentry\Throttling\UserBannedException $e) {
            return Response::json(array('logged' => false, 'errorMessage' => trans('syntara::all.messages.banned'), 'errorType' => 'danger'));
        } catch (\RuntimeException $e) {
            return Response::json(array('logged' => false, 'errorMessage' => trans('syntara::all.messages.login-failed'), 'errorType' => 'danger'));
        }

        return Response::json(array('logged' => true));
    }

    /**
    * Logout user
    */
    public function getLogout()
    {
        Sentry::logout();

        return Redirect::route('indexDashboard');
    }

    /**
    * Access denied page
    */
    public function getAccessDenied()
    {
        $this->layout = View::make(Config::get('syntara::views.error'), array('message' => trans('syntara::all.messages.denied')));
        $this->layout->title = trans('syntara::all.titles.error');
        $this->layout->breadcrumb = Config::get('syntara::breadcrumbs.dashboard');
    }
}


<?php

namespace MrJuliuss\Syntara\Commands;

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Config;
use MrJuliuss\Syntara\Services\Validators\User as UserValidator;

class UserSeedCommand extends Command
{

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'create:user';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create new user in database';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        try {
            $email = $this->argument('email');
            $pass = $this->argument('password');
            $username = $this->argument('username');
            $groupName = $this->argument('group');

            $validator = new UserValidator(array(
                    'email'    => $email,
                    'pass' => $pass,
                    'username' => $username,
                ),
            'create');

            if(!$validator->passes()) {
                foreach($validator->getErrors() as $key => $messages) {
                    $this->info(ucfirst($key).' :');
                    foreach($messages as $message) {
                        $this->error($message);
                    }
                }
            } else {
                // Create the user
                $user = \Sentry::getUserProvider()->create(array(
                    'email'    => $email,
                    'password' => $pass,
                    'username' => $username,
                ));

                $activationCode = $user->getActivationCode();
                $user->attemptActivation($activationCode);

                if($groupName !== NULL) {
                    $group = \Sentry::getGroupProvider()->findByName($groupName);

                    $user->addGroup($group);
                }

                $this->info('User created with success');
            }
        } catch (\Cartalyst\Sentry\Users\UserExistsException $e) {
            $this->error('User already exists !');
        } catch (\Cartalyst\Sentry\Groups\GroupNotFoundException $e) {
            $this->error('Group '.$groupName.' does not exists !');
        }
    }

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return array(
            array('username', InputArgument::REQUIRED, 'User name'),
            array('email',    InputArgument::REQUIRED, 'User email'),
            array('password', InputArgument::REQUIRED, 'User password'),
            array('group', InputArgument::OPTIONAL, 'Group')
        );
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return array();
    }

}


<?php namespace MrJuliuss\Syntara\Controllers;

use MrJuliuss\Syntara\Controllers\BaseController;
use MrJuliuss\Syntara\Services\Validators\Permission as PermissionValidator;
use PermissionProvider;
use View;
use Config;
use Input;
use Response;
use Request;
use URL;

class PermissionController extends BaseController
{
    /**
    * List of permissions
    */
    public function getIndex()
    {
        $permissions = PermissionProvider::createModel();
        $permissionId = Input::get('permissionIdSearch');
        if(!empty($permissionId)) {
            $permissions = $permissions->where('id', $permissionId);
        }
        $permissionName = Input::get('permissionNameSearch');
        if(!empty($permissionName)) {
            $permissions = $permissions->where('name', 'LIKE', '%'.$permissionName.'%');
        }
        $permissionValue = Input::get('permissionValueSearch');
        if(!empty($permissionValue)) {
            $permissions = $permissions->where('value', 'LIKE', '%'.$permissionValue.'%');
        }

        $permissions = $permissions->paginate(Config::get('syntara::config.item-perge-page'));

        // ajax request : reload only content container
        if(Request::ajax()) {
            $html = View::make(Config::get('syntara::views.permissions-list'), array('permissions' => $permissions))->render();

            return Response::json(array('html' => $html));
        }

        $this->layout = View::make(Config::get('syntara::views.permissions-index'), array('permissions' => $permissions));
        $this->layout->title = trans('syntara::permissions.titles.list');
        $this->layout->breadcrumb = Config::get('syntara::breadcrumbs.permissions');
    }

    /**
     * Show new permission view
     */
    public function getCreate()
    {
        $this->layout = View::make(Config::get('syntara::views.permission-create'));
        $this->layout->title = trans('syntara::permissions.titles.new');
        $this->layout->breadcrumb = Config::get('syntara::breadcrumbs.create_permission');
    }

    /**
     * Create new permission
     */
    public function postCreate()
    {
        try {
            $validator = new PermissionValidator(Input::all());
            if(!$validator->passes()) {
                return Response::json(array('permissionCreated' => false, 'errorMessages' => $validator->getErrors()));
            }

            // create permission
            $permission = PermissionProvider::createPermission(Input::all());
        } catch (\MrJuliuss\Syntara\Models\Permissions\NameRequiredException $e) {} catch (\MrJuliuss\Syntara\Models\Permissions\ValueRequiredException $e) {} catch (\MrJuliuss\Syntara\Models\Permissions\PermissionExistsException $e) {
            return json_encode(array('permissionCreated' => false, 'message' => trans('syntara::permissions.messages.exists'), 'messageType' => 'danger'));
        }

        return json_encode(array('permissionCreated' => true, 'redirectUrl' => URL::route('listPermissions')));
    }

    /**
    * View permission
    * @param int $permissionId
    */
    public function getShow($permissionId)
    {
        try {
            $permission = PermissionProvider::findById($permissionId);

            $this->layout = View::make(Config::get('syntara::views.permission-edit'), array(
                'permission' => $permission,
            ));
            $this->layout->title = 'Permission '.$permission->getName();
            $this->layout->breadcrumb = array(
                    array(
                        'title' => trans('syntara::breadcrumbs.permissions'),
                        'link' => URL::route('listPermissions'),
                        'icon' => 'glyphicon-ban-circle'
                    ),
                    array(
                     'title' => $permission->getName(),
                     'link' => URL::current(),
                     'icon' => ''
                    )
            );
        } catch (\MrJuliuss\Syntara\Models\Permissions\PermissionNotFoundException $e) {
            $this->layout = View::make(Config::get('syntara::views.error'), array('message' => trans('syntara::permissions.messages.not-found')));
        }
    }

    /**
    * Update permission
    * @param int $permissionId
    * @return Response
    */
    public function putShow($permissionId)
    {
        try {
            $validator = new PermissionValidator(Input::all());
            if(!$validator->passes()) {
                return Response::json(array('permissionUpdated' => false, 'errorMessages' => $validator->getErrors()));
            }

            // Find the permission using the permission id
            $permission = PermissionProvider::findById($permissionId);
            $permission->fill(Input::all());

            // Update the permission
            if($permission->save()) {
                return Response::json(array('permissionUpdated' => true, 'message' => trans('syntara::permissions.messages.update-success'), 'messageType' => 'success'));
            } else {
                return Response::json(array('permissionUpdated' => false, 'message' => trans('syntara::permissions.messages.update-fail'), 'messageType' => 'danger'));
            }
        } catch (\MrJuliuss\Syntara\Models\Permissions\PermissionExistsException $e) {
            return Response::json(array('permissionUpdated' => false, 'message' => trans('syntara::permissions.messages.exists'), 'messageType' => 'danger'));
        }
    }

    /**
    * Delete a permission
    */
    public function delete($permissionId)
    {
        try {
            $permission = PermissionProvider::findById($permissionId);
            $permission->delete();
        } catch (\MrJuliuss\Syntara\Models\Permissions\PermissionNotFoundException $e) {
            return Response::json(array('deletePermission' => false, 'message' => trans('syntara::permissions.messages.not-found'), 'messageType' => 'danger'));
        }

        return Response::json(array('deletePermission' => true, 'message' => trans('syntara::permissions.messages.remove-success'), 'messageType' => 'success'));
    }
}


<?php
namespace Dompdf\View;

use Cake\View\View;
use Dompdf\Dompdf;
use Dompdf\FontMetrics;
use Cake\Event\EventManager;
use Cake\Network\Request;
use Cake\Network\Response;

class PdfView extends View {

	private $config = [
		'dpi'				=> 192,
		'isRemoteEnabled'	=> true,
		'size'				=> 'A4',
		'orientation'		=> 'portrait',
		'render'			=> 'download',
		'filename' 			=> 'document',
        'paginate'          => false,
	];

    private $pdf;

    private $_pagination = [
        'x'     => 0,
        'y'     => 0,
        'font'  => null,
        'size'  => 12,
        'text'  => "{PAGE_NUM} / {PAGE_COUNT}",
        'color' => [0,0,0],
    ];

    public function initialize() {
        parent::initialize();
        $this->loadHelper('Dompdf.Dompdf');
    }

	public function __construct(Request $request = null, Response $response = null, EventManager $eventManager = null, array $viewOptions = []) {
		parent::__construct($request, $response, $eventManager, $viewOptions);

		if ( isset($viewOptions['config']) )
			$this->config = array_merge($this->config, $viewOptions['config']);
    }

	public function render($view = null, $layout = null) {

		$this->pdf = new Dompdf($this->config);
		$this->pdf->setPaper($this->config['size'], $this->config['orientation']);

        $pdf = $this->pdf;

        $this->set(compact('pdf'));

		$this->pdf->loadHtml(parent::render($view, $layout));

		$this->pdf->render();

        if ( is_array($this->config['paginate']) ) {
            $this->paginate();
        }

		switch ($this->config['render']) {
            case 'browser':
            case 'stream':
                return $this->pdf->output();

			case 'upload':
                $output = $this->pdf->output();
                if ( ! file_put_contents($this->config['upload_filename'], $output) )
                    return false;

                return $output;

			default: return $this->pdf->stream($this->config['filename']);
		}
	}

    /**
     * Write pagination on the pdf
     */
    private function paginate() {
        $canvas = $this->pdf->get_canvas();
        $c = array_merge($this->_pagination, $this->config['paginate']);
        $canvas->page_text($c['x'], $c['y'], $c['text'], $c['font'], $c['size'], $c['color']);
    }
}


<?php namespace MrJuliuss\Syntara\Models\Permissions;

use Illuminate\Database\Eloquent\Model;
use MrJuliuss\Syntara\Models\Permissions\PermissionExistsException;
use MrJuliuss\Syntara\Models\Permissions\NameRequiredException;
use MrJuliuss\Syntara\Models\Permissions\ValueRequiredException;

class Permission extends Model
{

    /**
     * Model 'Permission' table
     * @var string
     */
    protected $table = 'permissions';

   /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = array('name','value', 'description');

    /**
     * The attributes that aren't mass assignable.
     *
     * @var array
     */
    protected $guarded = array('id');

    /**
     * Return the identifiant of the permission
     * @return int id of the permission
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Return the name of the permission
     * @return string name of the permission
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Return the value of the permission
     * @return string value of the permission
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * Return description of the permission
     * @return string description of the permission
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Saves the permission.
     *
     * @param  array  $options
     * @return bool
     */
    public function save(array $options = array())
    {
        $this->validate();

        return parent::save($options);
    }

    /**
     * Validate permissions
     * @return bool
     */
    public function validate()
    {
        if(!$name = $this->getName()) {
            throw new NameRequiredException("A name is required for a permission, none given.");
        }

        if(!$value = $this->getValue()) {
            throw new ValueRequiredException("A value is required for a permission, none given.");
        }

        // Check if the permission already exists
        $query = $this->newQuery();
        $persistedPermission = $query->where('value', '=', $value)->first();

        if($persistedPermission and $persistedPermission->getId() != $this->getId()) {
            throw new PermissionExistsException("A permission already exists with value [$value], values must be unique for permissions.");
        }

        return true;
    }
}


<?php

namespace MrJuliuss\Syntara\Commands;

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Sentry;

class InstallCommand extends Command
{

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'syntara:install';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Syntara install command';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function fire()
    {
        $this->info('## Syntara Install ##');

        // publish sentry config
        $this->call('config:publish', array('package' => 'cartalyst/sentry' ) );

        // publish syntara config
        $this->call('config:publish', array('package' => 'mrjuliuss/syntara' ) );

        // publish syntara assets
        $this->call('asset:publish', array('package' => 'mrjuliuss/syntara' ) );

        // run migrations
        $this->call('migrate', array('--env' => $this->option('env'), '--package' => 'cartalyst/sentry' ) );
        $this->call('migrate', array('--env' => $this->option('env'), '--package' => 'mrjuliuss/syntara' ) );

        // create admin group
        try {
            $this->info('Creating "Admin" group...');
            $group = Sentry::getGroupProvider()->create(array(
                'name'        => 'Admin',
                'permissions' => array(
                    'superuser' => 1
                ),
            ));

            $this->info('"Admin" group created with success');
        } catch (\Cartalyst\Sentry\Groups\GroupExistsException $e) {
            $this->info('"Admin" group already exists');
        }
    }
}


<?php
namespace Dompdf\View\Helper;

use Cake\View\Helper;
use Cake\View\View;

/**
 * Dompdf helper
 */
class DompdfHelper extends Helper
{
	public $helpers = ['Html'];
    protected $_defaultConfig = [];

    /**
     * Creates a link element for CSS stylesheets
     * @param  string $path : The name of a CSS style sheet
     * @param  bool $plugin : (true) add a plugin css file || (false) add a file in webroot/css /// default : false
     * @return string <link>
     */
    public function css($path, $plugin = false) {
        $path = ($plugin) ? "dompdf/css/{$path}" : "css/{$path}";
    	return "<link rel=\"stylesheet\" href=\"{$path}.css\">";
    }

    /**
     * Générate an image
     * @param  string $path : Path to the image file, relative to the app/webroot/img/ directory
     * @param  array  $options : Array of HTML attributes
     * @return string
     */
    public function image($path, $options = []) {

    	$options['src'] = "img/{$path}";
    	$options['alt'] = (isset($options['alt'])) ? $options['alt'] : '';

    	return $this->Html->tag('img', null, $options);
    }

    /**
     * Generate a page break
     * @return string

     */
    public function page_break() {
        return $this->Html->tag('div', null, ['class' => 'page_break']);
    }

    /**
     * Write page number (use in header or footer)
     * @return string
     */
    public function page_number() {
        return $this->Html->tag('span', null, ['class' => 'page_number']);
    }
}


<?php

namespace AdobeMarketingCloud\Api;

/**
 * Api calls for getting data about permissions.
 *
 * @link      https://developer.omniture.com/en_US/documentation/omniture-administration/c-api-admin-methods-permissions
 * @author    Brent Shaffer <bshafs at gmail dot com>
 * @license   MIT License
 */
class Permissions extends SuiteApi
{
    /**
     * Internal method to authenticate a set of credentials
     * Credentials can be found in the SiteCatalyst Admin Console
     *   Admin > Admin Console > Company > Web Services
     *
     * @param   array $username  - the web services username
     * @param   array $password  - the web services shared secret
     * @return  boolean - whether or not the authentication was successful
     */
    public function authenticate($username, $password)
    {
        $response = $this->post('Permissions.Authenticate', array(
            'login'    => $username,
            'password' => $password,
        ));

		return $this->returnResponse($response);
    }
}


<?php

namespace AdobeMarketingCloud\Auth;

use AdobeMarketingCloud\AuthInterface;

/**
 * Performs requests on AdobeMarketingCloud API. API documentation should be self-explanatory.
 *
 * @author    Brent Shaffer <bshafs at gmail dot com>
 * @license   MIT License
 */
class Wsse implements AuthInterface
{
    private $username;
    private $secret;
    private $algorithm;

    public function authenticate($username, $secret, $algorithm = null)
    {
        $this->username = $username;
        $this->secret = $secret;
        $this->algorithm = $algorithm;
    }

    public function setAuthHeadersAndParameters(array $headers, array $parameters, array $options = array())
    {
        if(!$this->username || !$this->secret) {
            throw new Exception("username and secret must be set before making a request");
        }

        $nonce = $this->getNonce();
        $created = gmdate('c');

        if (empty($this->algorithm)) {
            $algorithm = 'sha1';
        } else {
            $algorithm = $this->algorithm;
        }

        $digest = base64_encode(hash($algorithm,$nonce.$created.$this->secret,true));
        $b64nonce = base64_encode($nonce);

        $header = sprintf('X-WSSE: UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"',
            $this->username,
            $digest,
            $b64nonce,
            $created
        );

        if (!empty($this->algorithm)) {
            $header = sprintf('%s, Algorithm="%s"', $header, $this->algorithm);
        }

        $headers[] = $header;

        return array($headers, $parameters);
    }

    /**
     * Uses UUID Version 4
     * @see http://en.wikipedia.org/wiki/Universally_unique_identifier
     */
    protected function getNonce()
    {
        return sprintf('%04x%04x-%04x-%03x4-%04x-%04x%04x%04x',
            mt_rand(0, 65535), mt_rand(0, 65535),
            mt_rand(0, 65535),
            mt_rand(0, 4095),
            bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '01', 6, 2)),
            mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)
        );
    }
}


<?php

namespace AdobeMarketingCloud;

/**
 * This class is designed to ease the task of parsing the arguments that are provided
 * to scripts from the command line. It supports several advanced features including
 * support for both short and long options, optional and/or required parameter checking,
 * automatic callback execution, and pretty printing of usage messages.
 *
 * @author  Michael J. I. Jackson <mjijackson@gmail.com>
 */
class OptionParser
{

    /**
     * The current version of OptionParser.
     *
     * @var string
     */
    const VERSION = '0.4';

    /**#@+
     * Configuration constant.
     *
     * @var int
     */
    /**
     * Use to stop parsing arguments when a double hyphen (--) is found.
     */
    const CONF_DASHDASH = 1;

    /**
     * Use to ignore case when parsing flags.
     */
    const CONF_IGNORECASE = 2;

    /**
     * All configuration options.
     */
    const CONF_ALL = 3;
    /**#@-*/

    /**#@+
     * Parameter constant.
     *
     * @var int
     */
    /**
     * Used for a parameter that is not required.
     */
    const PARAM_NOTREQUIRED = 0;

    /**
     * Used for a parameter that is required.
     */
    const PARAM_REQUIRED = 1;

    /**
     * Used for a parameter that is optional.
     */
    const PARAM_OPTIONAL = 2;
    /**#@-*/

    /**
     * The configuration flag.
     *
     * @var int
     */
    protected $_config = 0;

    /**
     * The name of the program, as parsed from the arguments.
     *
     * @var string
     */
    protected $_programName;

    /**
     * Contains usage messages that should go at the beginning of the usage message.
     *
     * @var array
     */
    protected $_head = array();

    /**
     * Contains usage messages that should go at the end of the usage message.
     *
     * @var array
     */
    protected $_tail = array();

    /**
     * The parsed options. This is only available after {@link parse()} is called.
     *
     * @var array
     */
    protected $_options = array();

    /**
     * The rules for this parser. Each rule is an array with three keys: description,
     * callback, and required.
     *
     * @var array
     */
    protected $_rules = array();

    /**
     * A map of flags to the index in the {@link $_rules} array that contains the
     * corresponding rule for that flag.
     *
     * @var array
     */
    protected $_flags = array();

    /**
     * Constructor.
     *
     * @param   int     $config     An optional configuration flag
     */
    public function __construct($config=null)
    {
        if (is_int($config)) {
            $this->setConfig($config);
        }
    }

    /**
     * Alias for {@link setOption()}.
     *
     * @return  void
     */
    public function __set($flag, $value)
    {
        $this->setOption($flag, $value);
    }

    /**
     * Alias for {@link getOption()}.
     *
     * @return  mixed
     */
    public function __get($flag)
    {
        return $this->getOption($flag);
    }

    /**
     * Returns true if an option for the given flag is set.
     *
     * @return  bool
     */
    public function __isset($flag)
    {
        try {
            $this->checkFlag($flag);
        } catch (\Exception $e) {
            return false;
        }

        $ruleIndex = $this->_flags[$flag];

        return isset($this->_options[$ruleIndex]);
    }

    /**
     * Unsets the given flag in this parser. Options for this flag
     * will no longer be available.
     *
     * @return  bool
     */
    public function __unset($flag)
    {
        unset($this->_flags[$flag]);
    }

    /**
     * Sets the configuration flag.
     *
     * @param   int
     */
    public function setConfig($config)
    {
        $this->_config = $config;
    }

    /**
     * Gets the configuration flag.
     *
     * @return  int
     */
    public function getConfig()
    {
        return $this->_config;
    }

    /**
     * Gets the program name that was used to execute this script.
     *
     * @return  string
     */
    public function getProgramName()
    {
        return $this->_programName;
    }

    /**
     * Sets the $value of the option for the given $flag.
     *
     * @param   string  $flag       The name of the flag
     * @param   mixed   $value      The new value for the option
     * @return  void
     */
    public function setOption($flag, $value)
    {
        $this->checkFlag($flag);
        $ruleIndex = $this->_flags[$flag];
        $this->_options[$ruleIndex] = $value;
    }

    /**
     * Gets the value of the option for the given $flag.
     *
     * @param   string  $flag       The name of the flag
     * @return  mixed
     */
    public function getOption($flag)
    {
        $this->checkFlag($flag);
        $ruleIndex = $this->_flags[$flag];

        if (array_key_exists($ruleIndex, $this->_options)) {
            return $this->_options[$ruleIndex];
        } else {
            return false;
        }
    }

    /**
     * Gets the values of all options as an associative array.
     *
     * @return Array
     */
    public function getAllOptions() {
      $options = array();

      foreach($this->_flags as $option => $index) {
        if(!empty($this->_options[$index])) {
          $options[$option] = $this->_options[$index];
        }
      }

      return $options;
    }

    /**
     * Add a usage message that will be displayed before the list of flags in the
     * output of {@link getUsage()}.
     *
     * @param   string  $message    The usage message
     * @return  void
     */
    public function addHead($message)
    {
        $this->_head[] = $message;
    }

    /**
     * Add a usage message that will be displayed after the list of flags in the
     * output of {@link getUsage()}.
     *
     * @param   string  $message    The usage message
     * @return  void
     */
    public function addTail($message)
    {
        $this->_tail[] = $message;
    }

    /**
     * Gets a usage message for this parser. The optional $maxWidth parameter can be
     * used to specify with width at which the message should wrap.
     *
     * @param   int
     * @return  string
     */
    public function getUsage($maxWidth=80)
    {
        $head = wordwrap(implode('', $this->_head), $maxWidth);
        $tail = wordwrap(implode('', $this->_tail), $maxWidth);

        # determine if there are any short flags
        $hasShortFlag = false;
        $allFlags = array_keys($this->_flags);
        foreach ($allFlags as $flag) {
            if (strlen($flag) == 1) {
                $hasShortFlag = true;
                break;
            }
        }

        $maxFlagsWidth = 0;
        $flagDescriptions = array();
        foreach ($this->_rules as $ruleIndex => $rule) {
            $flagList = array_keys($this->_flags, $ruleIndex);
            if (empty($flagList)) {
                continue;
            }
            usort($flagList, array($this, 'compareFlags'));
            $flags = array();
            $short = false;
            foreach ($flagList as $flag) {
                if (strlen($flag) == 1) {
                    $flags[] = "-$flag";
                    $short = true;
                } else {
                    $flags[] = "--$flag";
                }
            }
            $flags = implode(', ', $flags);
            if ($hasShortFlag && !$short) {
                $flags = "    $flags";
            }
            $maxFlagsWidth = max(strlen($flags), $maxFlagsWidth);
            $flagDescriptions[$flags] = $rule['description'];
        }

        $options = array();
        $format = " %-{$maxFlagsWidth}s  ";
        foreach ($flagDescriptions as $flags => $description) {
            $wrap = wordwrap($description, $maxWidth - ($maxFlagsWidth + 3));
            $lines = explode("\n", $wrap);
            $option = array(sprintf($format, $flags) . array_shift($lines));
            foreach ($lines as $line) {
                $option[] = str_repeat(' ', $maxFlagsWidth + 3) . $line;
            }
            $options[] = implode("\n", $option);
        }

        $usage = $head . "\n" . implode("\n", $options) . "\n" . $tail;

        return $usage;
    }

    /**
     * Adds a rule to this option parser. A rule consists of some flags that have
     * an optional description and/or callback function associated with them. The first
     * argument to this function should be the rule flags in a string seperated by a
     * pipe (|) character. The string may end in a colon (:) to indicate that the flag
     * accepts an optional parameter, or two colons (::) for a required parameter.
     * Remaining arguments may be a description, a callback function, both, or neither.
     *
     * If a description is given it will be used in the usage message for this parser
     * when describing this rule. If a callback function is given, it will be called when
     * any of the flags for this option are encountered in the arguments. Some examples
     * follow:
     *
     * <code>
     * $parser = new OptionParser;
     * $parser->addRule('a');
     * $parser->addRule('long-option');
     * $parser->addRule('b::'); // "b" with required parameter
     * $parser->addRule('q|quiet', "Don't output to the console"); // "q" or "quiet" with description
     * $parser->addRule('c', 'my_callback'); // my_callback will be called if the "c" flag is used
     * $parser->addRule('o|output:'); // "o" or "output" with optional parameter
     * </code>
     *
     * @param   string  $flags  The rule flags
     * @param   mixed   ...     The rule description or callback
     * @return  void
     */
    public function addRule()
    {
        $args = func_get_args();
        $rule = array(
            'description'   => '',
            'callback'      => null,
            'required'      => self::PARAM_NOTREQUIRED
        );

        $flags = array_shift($args);

        if ($this->_config & self::CONF_IGNORECASE) {
            $flags = strtolower($flags);
        }

        if (preg_match('/::?$/', $flags, $match)) {
            if (strlen($match[0]) == 2) {
                $rule['required'] = self::PARAM_REQUIRED;
            } else {
                $rule['required'] = self::PARAM_OPTIONAL;
            }
            $flags = rtrim($flags, ':');
        }

        # consume the remaining arguments
        while (count($args)) {
            $arg = array_pop($args);
            if (is_callable($arg)) {
                $rule['callback'] = $arg;
            } elseif (is_string($arg)) {
                $rule['description'] = $arg;
            }
        }

        $ruleIndex = count($this->_rules);

        foreach (explode('|', $flags) as $flag) {
            if ($flag) {
                $this->_flags[$flag] = $ruleIndex;
            }
        }

        $this->_rules[] = $rule;
    }

    /**
     * Gets the rule associated with the given flag.
     *
     * @return  array
     */
    public function getRule($flag)
    {
        try {
            $this->checkFlag($flag);
        } catch (Exception $e) {
            return null;
        }

        $ruleIndex = $this->_flags[$flag];

        return $this->_rules[$ruleIndex];
    }

    /**
     * Gets all rules of this parser.
     *
     * @return  array
     */
    public function getRules()
    {
        return $this->_rules;
    }

    /**
     * Returns true if the given flag expects a parameter.
     *
     * @return  bool
     */
    public function expectsParameter($flag)
    {
        $rule = $this->getRule($flag);
        return $rule && $rule['required'] !== self::PARAM_NOTREQUIRED;
    }

    /**
     * Returns true if the given flag is required.
     *
     * @return  bool
     */
    public function isRequired($flag)
    {
        $rule = $this->getRule($flag);
        return $rule && $rule['required'] === self::PARAM_REQUIRED;
    }

    /**
     * Returns true if the given flag is optional.
     *
     * @return  bool
     */
    public function isOptional($flag)
    {
        $rule = $this->getRule($flag);
        return $rule && $rule['required'] === self::PARAM_OPTIONAL;
    }

    /**
     * Parses the given arguments according to this parser's rules. An exception will be
     * thrown if any rule is violated.
     *
     * Note: This function uses a reference to modify the given $argv array. If no argument
     * values are provided a copy of the global $argv array will be used.
     *
     * @param   array   $argv       The command line arguments
     * @return  array               Anything still present in $argv that wasn't matched by a rule
     * @throws  Exception
     */
    public function parse(array &$argv=null)
    {
        $this->_options = array();

        if ($argv === null) {
            if (isset($_SERVER['argv'])) {
                $argv = $_SERVER['argv'];
            } else {
                $argv = array();
            }
        }

        $this->_programName = array_shift($argv);

        for ($i = 0; $i < count($argv); $i++) {
            if (preg_match('/^(--?)([a-z][a-z\-]*)(?:=(.+)?)?$/i', $argv[$i], $matches)) {
                if (isset($matches[3])) {
                    # put parameter back on stack of arguments
                    array_splice($argv, $i, 1, $matches[3]);
                    $paramGiven = true;
                } else {
                    # throw away the flag
                    array_splice($argv, $i, 1);
                    $paramGiven = false;
                }

                $flag = $matches[2];
                if ($this->_config & self::CONF_IGNORECASE) {
                    $flag = strtolower($flag);
                }

                if ($matches[1] == '--') {
                    # long flag
                    $this->parseOption($flag, $argv, $i, $paramGiven);
                } else {
                    # short flag
                    foreach (str_split($flag) as $shortFlag) {
                        $this->parseOption($shortFlag, $argv, $i, $paramGiven);
                    }
                }

                # decrement the index for the flag that was taken
                $i--;
            } elseif ($argv[$i] == '--') {
                if ($this->_config & self::CONF_DASHDASH) {
                    # stop processing arguments
                    break;
                }
            }
        }

        return $argv;
    }

    /**
     * Extracts the option value for the given $flag from the arguments array.
     *
     * @param   string  $flag           The flag being parsed
     * @param   array   $argv           The argument values
     * @param   int     $i              The current index in the arguments array
     * @param   bool    $paramGiven     True if a parameter was given using the --flag=param syntax
     * @return  void
     */
    protected function parseOption($flag, array &$argv, $i, $paramGiven)
    {
        $this->checkFlag($flag);

        $ruleIndex = $this->_flags[$flag];
        $rule = $this->_rules[$ruleIndex];

        if ($rule['required'] == self::PARAM_REQUIRED) {
            if (isset($argv[$i]) && ($paramGiven || $this->isParam($argv[$i]))) {
                $slice = array_splice($argv, $i, 1);
                $param = $slice[0];
            } else {
                throw new \Exception("Option \"$flag\" requires a parameter");
            }
        } elseif ($rule['required'] == self::PARAM_OPTIONAL) {
            if (isset($argv[$i]) && ($paramGiven || $this->isParam($argv[$i]))) {
                $slice = array_splice($argv, $i, 1);
                $param = $slice[0];
            } else {
                $param = true;
            }
        } else {
            $param = true;
        }

        if (is_callable($rule['callback'])) {
            call_user_func($rule['callback'], $param);
        }

        $this->_options[$ruleIndex] = $param;
    }

    /**
     * Returns true if the given string is considered a parameter.
     *
     * @param   string
     * @return  bool
     */
    protected function isParam($string)
    {
        if ($this->_config & self::CONF_DASHDASH && $string == '--') {
            return false;
        }

        return !$this->isFlag($string);
    }

    /**
     * Returns true if the given string is considered a flag.
     *
     * @param   string
     * @return  bool
     */
    protected function isFlag($string)
    {
        return (bool) preg_match('/^--?[a-z][a-z\-]*$/i', $string);
    }

    /**
     * Ensures the given flag is able to be recognized by this parser.
     *
     * @return  void
     * @throws  \Exception
     */
    protected function checkFlag($flag)
    {
        if (!array_key_exists($flag, $this->_flags)) {
            throw new \Exception("Option \"$flag\" is not recognized");
        }
    }

    /**
     * Used as a callback function for {@link usort()} when comparing two
     * flags. Flags are compared for length. If the two flags being compared are
     * of equal length, the flag that appeared first in the specification will
     * return first.
     *
     * @param   string
     * @param   string
     * @return  int
     */
    protected function compareFlags($a, $b)
    {
        $lena = strlen($a);
        $lenb = strlen($b);

        if ($lena == $lenb) {
            # returning -1 here will keep strings of the same length in the
            # same order they originally were in
            return -1;
        }

        return $lena > $lenb ? 1 : -1;
    }

    /**
     * Alias for {@link getUsage()}.
     *
     * @return  string
     */
    public function toString()
    {
        return $this->getUsage();
    }

    /**
     * Alias for {@link toString()}.
     *
     * @return  string
     */
    public function __toString()
    {
        return $this->toString();
    }

}


<?php

namespace AdobeMarketingCloud\HttpClient;

use AdobeMarketingCloud\HttpClient;

/**
 * Performs requests on AdobeMarketingCloud API using SOAP
 *
 * @author    Brent Shaffer <bshafs at gmail dot com>
 * @license   MIT License
 */
class SoapClient extends HttpClient
{
    protected $soap;
    protected $types = array(
        'base64Binary'  => 'base64Binary',
        'boolean'       => 'boolean',
        'date'          => 'date',
        'dateTime'      => 'dateTime',
        'double'        => 'double',
        'int'           => 'int',
        'string'        => 'string',
    );
    protected $calls = array();

    protected function getSoapClient($options)
    {
        if (!isset($options['wsdl'])) {
            $options['wsdl'] = strtr(':protocol://:endpoint/admin/:api_version/?wsdl', array(
              ':api_version' => $this->options['api_version'],
              ':protocol'    => $this->options['protocol'],
              ':endpoint'    => $this->options['endpoint'],
            ));
        }

        $soapOptions = array(
            'trace'    => 1,
            'features' => SOAP_USE_XSI_ARRAY_TYPE
        );

        if (isset($options['proxy'])) {
            list($host, $port) = explode(':', $options['proxy']);
            $soapOptions['proxy_host'] = $host;
            $soapOptions['proxy_port'] = $port;
        }

        if (isset($options['soapopts'])) {
            $soapOptions += $options['soapopts'];
        }

        // create the soap client
        return new \SoapClient($options['wsdl'], $soapOptions);
    }

    /**
    * Send a request to the server, receive a response
    *
    * @param  string   $apiPath       Request API path
    * @param  array    $parameters    Parameters
    * @param  string   $httpMethod    HTTP method to use
    *
    * @return string   HTTP response
    */
    protected function doRequest($url, array $parameters = array(), $httpMethod = 'GET', array $options = array())
    {
        $headers = isset($options['headers']) ? $options['headers'] : array();

        if ($auth = $this->getAuthService()) {
            $options['url'] = $url;
            $options['http_method'] = $httpMethod;
            list($headers, $parameters) = $auth->setAuthHeadersAndParameters($headers, $parameters, $options);
        }

        // parse out the method being requested
        $parts = parse_url($url);
        parse_str($parts['query'], $query);
        $method = $query['method'];

        $soapHeaders = $this->createSoapAuthHeader($headers);
        $soap = $this->getSoapClient($options);

        $template = $this->getMethodParameters($soap, $method);
        $parameters = $this->orderParameters($parameters, $template);
        $error = null;
        $response = null;

        try {
            $response = $soap->__soapCall($method, $parameters, null, $soapHeaders);
        } catch (SoapFault $e) {
            if ($e->faultcode == 'Server') {
                $error = $e->getMessage();
            } else {
                $response = array(
                    'error' => $e->faultcode == 'Client' ? 'Bad Request' : $e->faultcode,
                    'error_description' => $e->getMessage(),
                );
            }
        }

        return array(
            'response' => $soap->__getLastResponse(),
            'response_obj' => $response,
            'headers'  => $soap->__getLastResponseHeaders(),
            'errorMessage' => $error,
        );
    }

    protected function decodeResponse($response)
    {
        return $this->soapObjectToArray($response['response_obj']);
    }

    private function soapObjectToArray($obj)
    {
        if ($obj instanceof \SoapVar) {
            return $obj->enc_value;
        }

        if ($obj instanceof \stdClass) {
            $obj = (array) $obj;
        } elseif (!is_array($obj)) {
            return $obj;
        }

        // from here on, the object is an array
        foreach ($obj as $key => $value) {
            $obj[$key] = $this->soapObjectToArray($value);
        }

        return json_decode(json_encode($obj), true);
    }

    protected function createSoapAuthHeader($headers)
    {
        $soapHeaders = array();

        if (!isset($headers[0])) {
            throw new Exception('No auth headers set');
        }

        if (preg_match('/X-WSSE: UsernameToken Username="(.*)", PasswordDigest="(.*)", Nonce="(.*)", Created="(.*)"/', $headers[0], $matches)) {
            $wsseHeader = <<<EOF
<wsse:Security SOAP-ENV:mustUnderstand="1" xmlns:wsse="http://www.omniture.com">
    <wsse:UsernameToken wsse:Id="User">
         <wsse:Username>%s</wsse:Username>
         <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">%s</wsse:Password>
         <wsse:Nonce>%s</wsse:Nonce>
         <wsse:Created>%s</wsse:Created>
    </wsse:UsernameToken>
</wsse:Security>
EOF;

            $wsseHeader = sprintf($wsseHeader, $matches[1], $matches[2], $matches[3], $matches[4]);

            $soapVar = new \SoapVar($wsseHeader, XSD_ANYXML);

            $soapHeaders[] = new \SoapHeader("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", $soapVar);
        } else {
            throw new Exception('Invalid headers provided');
        }

        return $soapHeaders;
    }

    /**
     * this is necessary because order matters with the PHP SoapClient
     * Essentially, we fill in all the parameters with null values
     */
    protected function orderParameters($parameters, $template)
    {
        foreach ($parameters as $key => $value) {
            // if a parameter appears to be a nested type
            if (is_array($value) && !empty($value) && !isset($value[0])) {
                $parameters[$key] = $this->orderParameters($parameters[$key], $template[$key]);
            }
        }

        $defaults = array();

        foreach ($template as $key => $type) {
            $defaults[$key] = null;
        }

        $parameters = array_merge(
            $defaults,
            $parameters
        );

        // ensure there are no extra parameters
        $parameters = array_intersect_key($parameters, $template);

        return $parameters;
    }

    protected function getMethodParameters(\SoapClient $soap, $method)
    {
        foreach($soap->__getTypes() as $type) {
            $this->parseTypeDefinition($type);
        }

        foreach($soap->__getFunctions() as $call) {
            $this->parseCallDefinition($call);
        }

        if (isset($this->calls[$method])) {
            return $this->resolveTypes($this->calls[$method]);
        }

        return array();
    }

    protected function parseTypeDefinition($type)
    {
        //Simple Type
        if (preg_match('/^(\w+) (\w+)$/', $type, $matches)) {
            $this->types[$matches[2]] = $matches[1];

            return;
        }

        //Array
        if (preg_match('/^(\w+) (\w+)\[\]$/', $type, $matches))
        {
            $this->types[$matches[2]] = array(
                'array' => $matches[1]
            );

            return;
        }

        //Struct
        $type = str_replace("\n","", $type);
        $type = str_replace("struct ", "", $type);
        $type = str_replace(" {", "", $type);
        $type = str_replace("}", "", $type);
        $type = str_replace(";", "", $type);
        $type = explode(" ", $type);

        $fields = array();

        for($i=1; $i<count($type); $i+=2) {
            $fields[$type[$i+1]] = $type[$i];
        }

        $this->types[$type[0]] = array(
            'struct' => $fields
        );
    }

    protected function parseCallDefinition($call)
    {
        if (substr($call, 0, 4) == "list") {
            $call = "list".trim(substr($call, strpos($call, ")")));
        }

        $call = str_replace("(", " ", $call);
        $call = str_replace(")", "", $call);
        $call = str_replace(",", "", $call);
        $call = explode(" ", $call);

        $request = array();

        for($i=2; $i+1<count($call); $i+=2) {
            $request[str_replace("$", "", $call[$i+1])] = $call[$i];
        }

        $this->calls[$call[1]] = $request;
    }

    protected function resolveTypes($fields, $ignore = array())
    {
        $resolved = array();

        foreach($fields as $name => $type) {
            $resolved[$name] = $this->resolveType($type, $ignore);
        }

        return $resolved;
    }

    protected function resolveType($type, $ignore = array())
    {
        if (array_key_exists($type, $this->types)) {
            $typeDef = $this->types[$type];

            if (is_array($typeDef)) {
                if (in_array($type, $ignore)) {
                    return "($type)";
                }

                if (isset($typeDef['array'])) {
                    $typeDef = array($this->resolveType($typeDef['array'], array_merge($ignore, array($type))));
                } else if (isset($typeDef['struct'])) {
                    $typeDef = $this->resolveTypes($typeDef['struct'], array_merge($ignore, array($type)));
                } else {
                    throw new Exception(sprintf('Invalid type: %s', $type));
                }
            }

            if (!is_array($typeDef) && $typeDef[0] != '(') {
                $typeDef = "($typeDef)";
            }

            return $typeDef;
        }

        throw new Exception(sprintf('Type not found: %s', $type));
    }
}


<?php

namespace AdobeMarketingCloud;

/**
 * Simple PHP AdobeMarketingCloud API
 *
 * @tutorial  http://github.com/Adobe-Digital-Marketing/adobe-digital-marketing-php-sdk/blob/master/README.md
 * @version   2.6
 * @author    Brent Shaffer <bshafs at gmail dot com>
 * @license   MIT License
 *
 * Website: http://github.com/Adobe-Digital-Marketing/adobe-digital-marketing-php-sdk
 * Tickets: http://github.com/Adobe-Digital-Marketing/adobe-digital-marketing-php-sdk/issues
 */
class Client
{
    /**
     * The request instance used to communicate with AdobeMarketingCloud
     * @var HttpClient
     */
    protected $httpClient  = null;

    /**
     * The list of loaded API instances
     * @var array
     */
    protected $apis     = array();

    /**
     * Use debug mode (prints debug messages)
     * @var bool
     */
    protected $debug;

    /**
     * Instanciate a new AdobeMarketingCloud client
     *
     * @param  HttpClientInterface $httpClient custom http client
     */
    public function __construct(HttpClientInterface $httpClient = null)
    {
        if (null === $httpClient) {
            $this->httpClient = new HttpClient\Curl();
        } else {
            $this->httpClient = $httpClient;
        }
    }

    /**
     * Authenticate a user for all next requests
     *
     * @param  string         $username      AdobeMarketingCloud Web Services username
     * @param  string         $secret        AdobeMarketingCloud Web Services secret
     * @return AdobeMarketingCloud               fluent interface
     */
    public function authenticate()
    {
        $auth = $this->getHttpClient()->getAuthService();

        $args = func_get_args(); // php 5.2 requires this be on a separate line

        call_user_func_array(array($auth, 'authenticate'), $args);

        return $this;
    }

    /**
     * De-authenticate a user for all next requests
     *
     * @return Api               fluent interface
     */
    public function deAuthenticate()
    {
        return $this->authenticate(null, null);
    }

    /**
     * Set the Auth service on your Http Client
     *
     * @param  AuthInterface     $auth      Auth Interface
     * @return Api               fluent interface
     */
    public function setAuthService(AuthInterface $auth)
    {
        $this->getHttpClient()->setAuthService($auth);

        return $this;
    }

    /**
     * Call any route, GET method
     * Ex: $api->get('repos/show/my-username/my-repo')
     *
     * @param   string  $route            the AdobeMarketingCloud route
     * @param   array   $parameters       GET parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    public function get($route, array $parameters = array(), $requestOptions = array())
    {
        return $this->getHttpClient()->get($route, $parameters, $requestOptions);
    }

    /**
     * Call any route, POST method
     * Ex: $api->post('repos/show/my-username', array('email' => 'my-new-email@provider.org'))
     *
     * @param   string  $route            the AdobeMarketingCloud route
     * @param   array   $parameters       POST parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    public function post($route, array $parameters = array(), $requestOptions = array())
    {
        return $this->getHttpClient()->post($route, $parameters, $requestOptions);
    }

    /**
     * Call any route, PUT method
     * Ex: $api->put('repos/show/my-username', array('email' => 'my-new-email@provider.org'))
     *
     * @param   string  $route            the AdobeMarketingCloud route
     * @param   array   $parameters       PUT parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    public function put($route, array $parameters = array(), $requestOptions = array())
    {
        return $this->getHttpClient()->put($route, $parameters, $requestOptions);
    }

    /**
     * Call any route, DELETE method
     * Ex: $api->delete('repos/show/my-username', array('email' => 'my-new-email@provider.org'))
     *
     * @param   string  $route            the AdobeMarketingCloud route
     * @param   array   $parameters       DELETE parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    public function delete($route, array $parameters = array(), $requestOptions = array())
    {
        return $this->getHttpClient()->delete($route, $parameters, $requestOptions);
    }

    /**
     * Get the httpClient
     *
     * @return  HttpClientInterface   an httpClient instance
     */
    public function getHttpClient()
    {
        return $this->httpClient;
    }

    /**
     * Inject another request
     *
     * @param   HttpClientInterface   a httpClient instance
     * @return  Api          fluent interface
     */
    public function setHttpClient(HttpClientInterface $httpClient)
    {
        $this->httpClient = $httpClient;

        return $this;
    }

    /**
     * Get the suite API
     * generic API to call get/post directly
     *
     * @return  Api\SuiteApi  the report API
     */
    public function getSuiteApi($options = array())
    {
        if(!isset($this->apis['suite']))
        {
            $this->apis['suite'] = new Api\SuiteApi($this, $options);
        }

        return $this->apis['suite'];
    }

    /**
     * Get the report API
     *
     * @param array $options
     * @return Api\Report
     */
    public function getReportApi($options = array())
    {
        if(!isset($this->apis['report']))
        {
            $this->apis['report'] = new Api\Report($this, $options);
        }

        return $this->apis['report'];
    }

    /**
     * Get the permissions API
     *
     * @return  Api\Permissions  the permissions API
     */
    public function getPermissionsApi($options = array())
    {
        if(!isset($this->apis['permissions']))
        {
            $this->apis['permissions'] = new Api\Permissions($this, $options);
        }

        return $this->apis['permissions'];
    }

    /**
     * Get the company API
     *
     * @return  Api\Company  the company API
     */
    public function getCompanyApi($options = array())
    {
        if(!isset($this->apis['company']))
        {
            $this->apis['company'] = new Api\Company($this, $options);
        }

        return $this->apis['company'];
    }

    /**
     * Get the OAuth API
     *
     * @return  Api\OAuth  the company API
     */
    public function getOAuthApi($options = array())
    {
        if(!isset($this->apis['oauth']))
        {
            $this->apis['oauth'] = new Api\OAuth($this, $options);
        }

        return $this->apis['oauth'];
    }

    /**
     * Inject another API instance
     *
     * @param   string                $name the API name
     * @param   AdobeMarketingCloud   $api  the API instance
     * @return  Client       fluent interface
     */
    public function setApi($name, ApiInterface $instance)
    {
        $this->apis[$name] = $instance;

        return $this;
    }

    /**
     * returns the most recent response for debugging purposes (see AdobeMarketingCloud\HttpClient::getLastResponse)
     */
    public function getLastResponse()
    {
        return $this->getHttpClient()->getLastResponse();
    }

    /**
     * Get any API
     *
     * @param   string                    $name the API name
     * @return  AdobeMarketingCloud\Api\Abstract     the API instance
     */
    public function getApi($name)
    {
        return $this->apis[$name];
    }

    public function setEndpoint($endpoint)
    {
        if (0 === strpos($endpoint, 'http')) {
            $parts = parse_url($endpoint);
            $endpoint = $parts['host'];
        }
        $this->getHttpClient()->setOption('endpoint', $endpoint);
    }

    public function getEndpoint()
    {
        return $this->getHttpClient()->getOption('endpoint');
    }

    public function __clone()
    {
        // Force a copy of $httpClient, otherwise
        // it will point to same object.
        $this->httpClient = clone $this->httpClient;
    }
}


<?php

namespace AdobeMarketingCloud\HttpClient;

use AdobeMarketingCloud\HttpClient;

/**
 * Performs requests on AdobeMarketingCloud API. API documentation should be self-explanatory.
 *
 * @author    Brent Shaffer <bshafs at gmail dot com>
 * @license   MIT License
 */
class Curl extends HttpClient
{
    /**
    * Send a request to the server, receive a response
    *
    * @param  string   $apiPath       Request API path
    * @param  array    $parameters    Parameters
    * @param  string   $httpMethod    HTTP method to use
    *
    * @return string   HTTP response
    */
    protected function doRequest($url, array $parameters = array(), $httpMethod = 'GET', array $options = array())
    {
        $curlOptions = array();
        $headers = isset($options['headers']) ? $options['headers'] : array();

        if ('POST' === $httpMethod) {
            $curlOptions += array(
                CURLOPT_POST  => true,
            );
        }
        elseif ('PUT' === $httpMethod) {
            $curlOptions += array(
                CURLOPT_POST  => true, // This is so cURL doesn't strip CURLOPT_POSTFIELDS
                CURLOPT_CUSTOMREQUEST => 'PUT',
            );
        }
        elseif ('DELETE' === $httpMethod) {
            $curlOptions += array(
                CURLOPT_CUSTOMREQUEST => 'DELETE',
            );
        }

        if ($auth = $this->getAuthService()) {
            $options['url'] = $url;
            $options['http_method'] = $httpMethod;
            list($headers, $parameters) = $auth->setAuthHeadersAndParameters($headers, $parameters, $options);
        }

        if (!empty($parameters))
        {
            if('GET' === $httpMethod)
            {
                $queryString = utf8_encode($this->buildQuery($parameters));
                $url .= '?' . $queryString;
            }
            else
            {
                switch ($options['content-type']) {
                    case 'form':
                        $headers[] = 'Content-Type: application/x-www-form-urlencoded';
                        $parameters = http_build_query($parameters);
                        break;
                    case 'json':
                    default:
                        $headers[] = 'Content-Type: application/json';
                        $parameters = json_encode($parameters);
                        break;
                }

                $curlOptions += array(
                    CURLOPT_POSTFIELDS  => $parameters,
                );
            }
        } else {
            $headers[] = 'Content-Length: 0';
        }

        switch ($options['format']) {
            case 'json':
                $headers[] = 'Accept: application/json';
                break;
            case 'xml':
                $headers[] = 'Accept: text/xml';
                break;
        }

        $this->debug('send '.$httpMethod.' request: '.$url);

        // format headers
        foreach ($headers as $i => $header) {
            if (is_string($i)) {
                $headers[] = "{$i}: $header";
                unset($headers[$i]);
            }
        }

        $curlOptions += array(
            CURLOPT_URL             => $url,
            CURLOPT_PORT            => $options['http_port'],
            CURLOPT_USERAGENT       => $options['user_agent'],
            CURLOPT_FOLLOWLOCATION  => $options['follow-location'],
            CURLOPT_RETURNTRANSFER  => true,
            CURLOPT_HTTPHEADER      => $headers,
            CURLOPT_SSL_VERIFYPEER  => !isset($options['verifyssl']) || $options['verifyssl'],
        );

        // handle timeout values less than 1 second
        if (is_int($options['timeout'])) {
            $curlOptions[CURLOPT_TIMEOUT] = $options['timeout'];
        } else if ($options['timeout'] > 0 && $options['timeout'] < 1) {
            $curlOptions[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
        }

        if (isset($options['curlopts']) && is_array($options['curlopts'])) {
            $curlOptions += $options['curlopts'];
        }

        if (isset($options['proxy'])) {
            $curlOptions[CURLOPT_PROXY] = $options['proxy'];
        }

        if (isset($options['sslversion'])) {
            $curlOptions[CURLOPT_SSLVERSION] = $options['sslversion'];
        } else {
            $curlOptions[CURLOPT_SSLVERSION] = 0;
        }

        $response = $this->doCurlCall($curlOptions);

        return $response;
    }

    /**
     * Get a JSON response and transform it to a PHP array
     *
     * @return  array   the response
     */
    protected function decodeResponse($response, $options = array())
    {
        // "false" means a failed curl request
        if (false === $response['response']) {
            $this->debug(print_r($response, true));
            return false;
        }
        return parent::decodeResponse($response, $options);
    }

    protected function doCurlCall(array $curlOptions)
    {
        $curl = curl_init();

        curl_setopt_array($curl, $curlOptions);

        $response = curl_exec($curl);
        $headers = curl_getinfo($curl);
        $errorNumber = curl_errno($curl);
        $errorMessage = curl_error($curl);

        if (isset($curlOptions[CURLOPT_HEADER]) && $curlOptions[CURLOPT_HEADER]) {
            $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
            $header = substr($response, 0, $header_size);
            $response = substr($response, $header_size);
            $headers['response'] = array();
            foreach (explode("\n", trim($header)) as $line) {
                if (false !== strpos($line, ':')) {
                    list($key, $value) = explode(':', $line, 2);
                    $headers['response_headers'][strtolower($key)] = trim($value);
                } else {
                    $headers['response_headers'][] = $line;
                }
            }
        }

        curl_close($curl);

        return compact('response', 'headers', 'errorNumber', 'errorMessage', 'curlOptions');
    }

    protected function buildQuery($parameters)
    {
        return http_build_query($parameters, '', '&');
    }

    protected function debug($message)
    {
        if ($this->options['debug']) {
            print $message."\n";
        }
    }
}


<?php

namespace AdobeMarketingCloud\Api;

use AdobeMarketingCloud\Api;

/**
 * Common base class for all APIs calling the 1.2,1.3, and 1.4-style methods of the API
 *
 * @author    Brent Shaffer <bshafs at gmail dot com>
 * @license   MIT License
 */
class SuiteApi extends Api
{
    /**
     * Call any path, GET method
     * Ex: $api->get('artist/biographies', array('name' => 'More Hazards More Heroes'))
     *
     * @param   string  $path             the AdobeMarketingCloud path
     * @param   array   $parameters       GET parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    public function get($method, array $parameters = array(), $requestOptions = array())
    {
        $parameters['method'] = $method;
        return parent::get($this->getSuitePath(), $parameters, $requestOptions);
    }

    /**
     * Call any path, POST method
     * Ex: $api->post('catalog/create', array('type' => 'artist', 'name' => 'My Catalog'))
     *
     * @param   string  $path             the AdobeMarketingCloud path
     * @param   array   $parameters       POST parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    public function post($method, array $parameters = array(), $requestOptions = array())
    {
        return parent::post($this->getSuitePath($method), $parameters, $requestOptions);
    }

    private function getSuitePath($method = null)
    {
        return sprintf('admin/%s/rest/%s', $this->client->getHttpClient()->getOption('api_version'), is_null($method) ? '' : '?method='.$method);
    }
}


<?php

namespace AdobeMarketingCloud;

/**
 * Abstract class for Api classes
 *
 * @author    Brent Shaffer <bshafs at gmail dot com>
 * @license   MIT License
 */
abstract class Api implements ApiInterface
{
    /**
    * The core AdobeMarketingCloud Client
    * @var Client
    */
    protected
        $client,
        $options = array();

    public function __construct(Client $client, $options = array())
    {
        $this->client  = $client;
        $this->options = $options;
    }

    /**
     * Call any path, GET method
     * Ex: $api->get('artist/biographies', array('name' => 'More Hazards More Heroes'))
     *
     * @param   string  $path             the AdobeMarketingCloud path
     * @param   array   $parameters       GET parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    protected function get($path, array $parameters = array(), $requestOptions = array())
    {
        return $this->client->get($path, $parameters, $requestOptions);
    }

    /**
     * Call any path, POST method
     * Ex: $api->post('catalog/create', array('type' => 'artist', 'name' => 'My Catalog'))
     *
     * @param   string  $path             the AdobeMarketingCloud path
     * @param   array   $parameters       POST parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    protected function post($path, array $parameters = array(), $requestOptions = array())
    {
        return $this->client->post($path, $parameters, $requestOptions);
    }

     /**
     * Call any path, PUT method
     * Ex: $api->put('segments/54321abcdef', array('name' => 'Best Segment Ever'))
     *
     * @param   string  $path             the AdobeMarketingCloud path
     * @param   array   $parameters       PUT parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    protected function put($path, array $parameters = array(), $requestOptions = array())
    {
        return $this->client->put($path, $parameters, $requestOptions);
    }

    /**
     * Call any path, DELETE method
     * Ex: $api->delete('calculatedMetrics/12345abcedf')
     *
     * @param   string  $path             the AdobeMarketingCloud path
     * @param   array   $parameters       POST parameters
     * @param   array   $requestOptions   reconfigure the request
     * @return  array                     data returned
     */
    protected function delete($path, array $parameters = array(), $requestOptions = array())
    {
        return $this->client->delete($path, $parameters, $requestOptions);
    }

    /**
    * Change an option value.
    *
    * @param string $name   The option name
    * @param mixed  $value  The value
    *
    * @return ApiAbstract the current object instance
    */
    public function setOption($name, $value)
    {
        $this->options[$name] = $value;

        return $this;
    }

    /**
    * Get an option value.
    *
    * @param  string $name The option name
    *
    * @return mixed  The option value
    */
    public function getOption($name, $default = null)
    {
        return isset($this->options[$name]) ? $this->options[$name] : $default;
    }

    /**
     * returns the most recent response for debugging purposes (see AdobeMarketingCloud\HttpClient::getLastResponse)
     */
    public function getLastResponse()
    {
        return $this->client->getLastResponse();
    }

    protected function returnResponse($response, $key = null)
    {
        if (!is_null($key) && isset($response[$key]) && !$this->getOption('raw')) {
            return $response[$key];
        }

        return $response;
    }
}


<?php

namespace AdobeMarketingCloud;

use AdobeMarketingCloud\Auth\Wsse;
use AdobeMarketingCloud\HttpClient\Exception;

/**
 * Performs requests on AdobeMarketingCloud API. API documentation should be self-explanatory.
 *
 * @author    Brent Shaffer <bshafs at gmail dot com>
 * @license   MIT License
 */
abstract class HttpClient implements HttpClientInterface
{
    /**
     * The request options
     * @var array
     */
    protected $options = array(
        'protocol'    => 'https',
        'api_version' => '1.4',
        'endpoint'    => 'api.omniture.com',
        'url'         => ':protocol://:endpoint/:path',
        'user_agent'  => 'adobe-digital-marketing-php-sdk (http://github.com/Adobe-Digital-Marketing)',
        'http_port'   => 443,
        'timeout'     => 20,
        'username'    => null,
        'secret'      => null,
        'format'      => 'json',
        'limit'       => false,
        'debug'       => false,
        'proxy'       => null,
        'content-type' => 'json',
        'follow-location' => true, // automatically follow "Location" header for 301 redirects
    );

    protected $auth;
    protected $lastResponse;

    /**
     * Instanciate a new request
     *
     * @param  array   $options  Request options
     */
    public function __construct(array $options = array(), AuthInterface $auth = null)
    {
        if (is_null($auth)) {
            $auth = new Wsse();
        }
        $this->auth = $auth;
        $this->configure($options);
    }

    /**
     * Configure the request
     *
     * @param   array               $options  Request options
     * @return  AdobeMarketingCloudApiRequest $this     Fluent interface
     */
    public function configure(array $options)
    {
        $this->options = array_merge($this->options, $options);

        return $this;
    }

    /**
     * Send a request to the server, receive a response
     *
     * @param  string   $url           Request url
     * @param  array    $parameters    Parameters
     * @param  string   $httpMethod    HTTP method to use
     * @param  array    $options        Request options
     *
     * @return string   HTTP response
     */
    abstract protected function doRequest($url, array $parameters = array(), $httpMethod = 'GET', array $options = array());

    /**
     * Send a GET request
     * @see send
     */
    public function get($path, array $parameters = array(), array $options = array())
    {
        return $this->request($path, $parameters, 'GET', $options);
    }

    /**
     * Send a POST request
     * @see send
     */
    public function post($path, array $parameters = array(), array $options = array())
    {
        return $this->request($path, $parameters, 'POST', $options);
    }

    /**
     * Send a PUT request
     * @see send
     */
    public function put($method, array $parameters = array(), array $options = array())
    {
        return $this->request($method, $parameters, 'PUT', $options);
    }

    /**
     * Send a DELETE request
     * @see send
     */
    public function delete($method, array $parameters = array(), array $options = array())
    {
        return $this->request($method, $parameters, 'DELETE', $options);
    }

    /**
     * Send a request to the server, receive a response,
     * decode the response and returns an associative array
     *
     * @param  string   $path           Requested API resource path
     * @param  array    $parameters     Parameters
     * @param  string   $httpMethod     HTTP method to use
     * @param  array    $options        Request options
     *
     * @return array                    Data
     */
    public function request($path, array $parameters = array(), $httpMethod = 'GET', array $options = array())
    {
        $options = array_merge($this->options, $options);

        // create full url
        $url = strtr($options['url'], array(
          ':api_version' => $this->options['api_version'],
          ':protocol'    => $this->options['protocol'],
          ':endpoint'    => $this->options['endpoint'],
          ':path'        => $path
        ));

        // get encoded response
        $response = $this->doRequest($url, $parameters, $httpMethod, $options);
        $this->lastResponse = $response; // for debugging (see getLastResponse)

        // decode response
        $response = $this->decodeResponse($response, $options);

        return $response;
    }

    /**
     * Change an option value.
     *
     * @param string $name   The option name
     * @param mixed  $value  The value
     *
     * @return The current object instance
     */
    public function setOption($name, $value)
    {
        $this->options[$name] = $value;

        return $this;
    }

    /**
     * Return the options array.
     *
     * @return array The httpclient options
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Get an option value.
     *
     * @param  string $name The option name
     *
     * @return mixed  The option value
     */
    public function getOption($name, $default = null)
    {
        return isset($this->options[$name]) ? $this->options[$name] : $default;
    }

    public function getAuthService()
    {
        return $this->auth;
    }

    public function setAuthService(AuthInterface $auth)
    {
        $this->auth = $auth;
    }

    /**
     * returns the most recent response for debugging purposes
     */
    public function getLastResponse()
    {
        return $this->lastResponse;
    }

    /**
     * Get a JSON response and transform it to a PHP array
     *
     * @return  array   the response
     * @throws Exception
     * @throws \LogicException
     */
    protected function decodeResponse($response, $options = array())
    {
        if (count($options) == 0) {
            $options = $this->options;
        }

        switch ($options['format'])
        {
            case 'json':
                $json = json_decode($response['response'], TRUE);
                if ($json === null) {
                    $debugPrintout = "";
                    if ($options['debug'] === TRUE) {
                        $debugPrintout = ": \n\n" . print_r($response, TRUE);
                    }

                    throw new Exception("Response is not in JSON format $debugPrintout");
                }

                return $json;

            case 'jsonp':
                throw new \LogicException("format 'jsonp' not yet supported by this library");

            case 'xml':
                return json_decode(json_encode(simplexml_load_string($response['response'])), true);

            case 'xspf':
                throw new \LogicException("format 'xspf' not yet supported by this library");

            case 'raw':
                return $response['response'];
        }

        throw new \LogicException(__CLASS__.' only supports json, json, xml, and xspf formats, '.$options['format'].' given.');
    }
}


<?php

namespace AdobeMarketingCloud\Api;

/**
 * Api calls for getting data about reports.
 *
 * @link      https://marketing.adobe.com/developer/documentation/analytics-reporting-1-4/get-started
 * @author    Brent Shaffer <bshafs at gmail dot com>
 * @license   MIT License
 */
class Report extends SuiteApi
{
    /**
     * Retrieve the elements to call from your report suite
     *
     * @param string $reportSuiteID
     * @param bool $returnAsIdArray
     *
     * @return array
     */
    public function getElements($reportSuiteID, $returnAsIdArray = false)
    {
        $response = $this->post('Report.GetElements', array('reportSuiteID' => $reportSuiteID));
        if ($returnAsIdArray) {
            $filtered = array();
            foreach ($response as $element) {
                $filtered[$element['id']] = $element['name'];
            }
            return array_unique($filtered);
        }
        return $this->returnResponse($response);
    }

    /**
     * Retrieve events for your report suites
     *
     * @param string $reportSuiteID
     * @param bool $returnAsIdArray
     *
     * @return array
     */
    public function getMetrics($reportSuiteID, $returnAsIdArray = false)
    {
        $response = $this->post('Report.GetMetrics', array('reportSuiteID' => $reportSuiteID));
        if ($returnAsIdArray) {
            $filtered = array();
            foreach ($response as $metric) {
                $filtered[$metric['id']] = $metric['name'];
            }
            return array_unique($filtered);
        }
        return $this->returnResponse($response);
    }

    /**
     * Submits a report request.
     *
     * https://marketing.adobe.com/developer/documentation/analytics-reporting-1-4/r-queue
     *
     * @param   array $reportDescription  the report description array
     * @return  array
     *   - reportID: The report ID of the queued report
     */
    public function queueReport($reportDescription)
    {
        $response = $this->post('Report.Queue', array(
            'reportDescription' => $reportDescription,
        ));

        return $this->returnResponse($response);
    }

    /**
     * Gets a report by report ID
     * https://marketing.adobe.com/developer/documentation/analytics-reporting-1-4/r-get
     *
     * @param   mixed     The report ID or report description
     * @return  tns:report      A structure containing the report data for the specified reportID
     */
    public function getReport($report)
    {
        if (is_numeric($report)) {
            // try to get the report
            $response = $this->retryWhileNotReady($report);
        } else {
            $response = $this->Report($report);
        }

        return $response;
    }

    /**
     * Cancels a previously submitted report request, and removes it from the processing queue.
     * https://marketing.adobe.com/developer/documentation/analytics-reporting-1-4/r-cancel
     *
     * @param   int     The report ID returned as part of the "queue report" request.
     * @return  int     Returns 1 if the operation is successful, or 0 if the operation failed.
     */
    public function cancelReport($reportId)
    {
        $response = $this->post('Report.Cancel', array(
            'reportID' => $reportId,
        ));

        return $this->returnResponse($response);
    }

    /**
     * Returns a list of reports in the specified company's report queue.
     * https://marketing.adobe.com/developer/documentation/analytics-reporting-1-4/r-getqueue-2
     *
     * @param   none
     * @return  tns:report_queue    A list of the company's currently queued report requests. SiteCatalyst determines the company by the authentication credentials provided with the request.
     */
    public function getQueue()
    {
        $response = $this->post('Report.GetQueue');

        return $this->returnResponse($response);
    }

    /**
     * Returns a report. Queues the report synchronously.
     * @see Report::queueAndGetReport()
     */
    public function Report($reportDescription)
    {
        $response = $this->queueReport($reportDescription);
        return $this->retryWhileNotReady($response['reportID']);
    }

    /**
     * Returns a report synchronously, calling getReport every two seconds until the report is ready
     *
     * @param $reportId
     * @return tns|bool
     */
    protected function retryWhileNotReady($reportId)
    {
        $attempts = 0;
        do {
            $report = $this->post('Report.Get', array(
                'reportID' => $reportId,
            ));
            $sleep = $this->getSleepSeconds(++$attempts, 50);
            if ($sleep !== false) {
                sleep($sleep);
            }
        } while (isset($report['error']) && $report['error'] == 'report_not_ready');

        if (isset($report['report'])) {
            $report = $report['report'];
        } else {
            throw new ReportError($report['error_description'], null);
        }

        return $report;
    }

    /**
    * Determines next sleep time for report queue checking.
    * Uses an incredibly complex backing off algorithm so that long requests don't have to check as often.
    *
    * @param    $attempts      int    The number of checks so far
    * @param    $maxAttempts    int User specified maximum number of checks
    *
    * @return    mixed to stop checking OR the number of seconds for the next sleep
    */
    protected function getSleepSeconds($attempts, $maxAttempts = null)
    {
        if ($maxAttempts && $attempts >= $maxAttempts) {
            return false;
        }

        // very complex.
        return $attempts * $attempts;
    }

    protected function returnResponse($response, $key = null)
    {
        if (array_key_exists('error', $response) && array_key_exists('error_description', $response)) {
            throw new ReportError($response['error'], $response['error_description']);
        }

        return parent::returnResponse($response, $key);
    }
}