Recently, I've been working on a system using the Phalcon PHP framework. And up until now, it's been plain sailing.
I seem to be having an issue when pulling data from a MySQL database using a model. Everything else seems to be working fine, but a few fields are returning empty data despite there being data within those fields in the MySQL table.
After scanning the database itself, I can't seem to find the reason why these particular fields are empty because similar fields are returned with the data intact.
I've inserted the model data into an array to make it easier for me to browse it and find out what's going on, and this is the result:
[applications] =>
[application_id] => 5
[application_user] => 1
[application_servers] => skybuild
[application_approved] =>
[application_denied] =>
[application_reviewed] =>
[application_approvals] => 0
[application_denials] => 0
[application_date] => 1470739996
[application_message] => This is just a test application to see whether the system is working.
[user] =>
[user_id] => 1
[user_fname] => Leo
[user_lname] => **********
[user_birthday] => ****-**-**
[user_email] => **********#**********.**.**
[user_uname] => Leo_V117
[user_upass] => ********************************
[user_account] => ********-****-****-****-************
[user_active] => Y
[user_banned] =>
[user_suspended] =>
[user_registered] =>
The fields in question are:
application
application_approved
application_denied
application_reviewed
user
user_banned
user_suspended
user_registered
The question is:
WHY are these particular fields returning empty data and, ultimately, HOW can I go about fixing it.
PHP Code
Bootstrap
<?php ########################
##############################
use Phalcon\Mvc\Application;
error_reporting(E_ALL);
try {
/**
* Define some useful constants
*/
define('BASE_DIR', dirname(__DIR__));
define('APP_DIR', BASE_DIR . '/app');
\Phalcon\Mvc\Model::setup(array(
'notNullValidations' => false
));
/**
* Read the configuration
*/
$config = include APP_DIR . '/config/config.php';
/**
* Read auto-loader
*/
include APP_DIR . '/config/loader.php';
/**
* Read services
*/
include APP_DIR . '/config/services.php';
/**
* Handle the request
*/
$application = new Application($di);
echo $application->handle()->getContent();
} catch (Exception $e) {
echo '<pre>';
print_r ( $e->getMessage() );
print_r ( nl2br( htmlentities( $e->getTraceAsString() ) ) );
echo '</pre>';
}
##############################
########################### ?>
Accounts
<?php ########################
##############################
namespace ProjectRogue\Models;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Validator\Uniqueness;
class Accounts extends Model {
public $user_id; // User ID
public $user_fname; // Field: First Name
public $user_lname; // Field: Last Name
public $user_birthday; // Field: Date of Birth
public $user_email; // Field: E-Mail
public $user_uname; // Field: Username
public $user_upass; // Field: Password
public $user_account; // Minecraft Account UUID
public $user_active; // Active
public $user_banned; // Banned
public $user_suspended; // Suspended
public $user_registered;
public function beforeValidationOnCreate() {
$this->user_active = 'N';
$this->user_banned = 'N';
$this->user_suspended = 'N';
$this->user_code = $this->generateCode();
$this->user_registered = time();
}
public function afterSave() {
if( $this->user_active == 'N' ) {
// Check E-Mail Status;
$email = new EmailConfirmation();
$email->email_user = $this->user_id;
$email->email_code = $this->generateCode();
$email->email_created = date("Y-m-d H:i:s", time());
if( $email->save() ) {
$this->getDI()->getFlash()->notice('A confirmation E-Mail has been sent to '.$this->user_email);
}
// Send Commands;
$service_servers = new ServiceServers();
$service_commands = array();
$servers = $service_servers::find();
$json_response['data']['servers_size'] = count($servers);
foreach( $servers as $server ) {
$service_commands[ $server->server_id ] = new ServiceCommands();
// Insert Data;
$service_commands[ $server->server_id ]->assign(array(
'command_server' => $server->server_token,
'command_body' => 'broadcast &6&l[player]&r &fhas just registered an account to our website!',
'command_player' => $this->user_account,
'command_player_online' => 'Y'
));
// Save;
$service_commands[ $server->server_id ]->save();
}
}
}
public function validation() {
$this->validate(new Uniqueness(array(
'field' => 'user_uname',
'message' => 'This Username is already in use.'
)));
$this->validate(new Uniqueness(array(
'field' => 'user_email',
'message' => 'This E-Mail is already in use.'
)));
$this->validate(new Uniqueness(array(
'field' => 'user_account',
'message' => 'This Minecraft Account is already in use.'
)));
return $this->validationHasFailed() != true;
}
}
##############################
########################### ?>
ApplicationStaff
<?php ########################
##############################
namespace ProjectRogue\Models;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Validator\Uniqueness;
class ApplicationsStaff extends Model {
public $application_id; // Application ID;
public $application_user; // Application User;
public $application_servers; // Application Servers;
public $application_approved;
public $application_denied;
public $application_reviewed;
public $application_approvals; // Application Approvals;
public $application_denials; // Application Denials;
public $application_date; // Application Date;
public $application_message; // Application Message;
public function beforeValidationOnCreate() {
$this->application_date = time();
$this->application_approvals = 0;
$this->application_denials = 0;
$this->application_approved = "N";
$this->application_denied = "N";
$this->application_reviewed = "N";
}
public function validation() {
return $this->validationHasFailed() != true;
}
}
##############################
########################### ?>
It actually turns out that the data from each model was cached in my service. Deleting said cache forced the models to return the data as expected.
Related
Firstly, I tried all the questions & answers related to this topic. Additionally and I tried related questions and try to solve it but no success. So please read my question thoroughly.
1) i want to create a custom controller on custom module in prestashop without tab.and get browser url Link.
2) how to create a controller url link with tocken on twig file.
i have successfully created module and installed in my PS.
i create a controller [Checkstatus.php]
file path module/mymodule/contollers/admin/Checkstatus.php
<?php
class CheckstatusController extends ModuleAdminController {
public function __construct()
{
$this->page_name = 'checkstatus'; // page_name and body id
echo "sfg";
parent::__construct();
}
public function init()
{
parenrt::init();
}
public function demoAction()
{
return $this->render('#Modules/your-module/templates/admin/demo.html.twig');
}
}
my Custom module
<?php
if (!defined('_PS_VERSION_')) {
exit;
}
class MyModule extends PaymentModule
{
public function __construct()
{
$this->name = 'MyCustomModule';
$this->tab = 'payments XYZ';
$this->version = '1.0';
$this->author = 'XYZ Technologies';
$this->bootstrap = true;
$this->displayName = 'XYZ';
$this->description = 'XYZ.';
$this->confirmUninstall = 'Are you sure you want to uninstall XYZ module?';
$this->ps_versions_compliancy = array('min' => '1.7.0', 'max' => _PS_VERSION_);
$this->allow_countries = array('CH', 'LI', 'AT', 'DE');
$this->allow_currencies = array('CHF', 'EUR');
parent::__construct();
}
/**
* Install this module and register the following Hooks:
*
* #return bool
*/
public function install()
{
if (Shop::isFeatureActive()) {
Shop::setContext(Shop::CONTEXT_ALL);
}
Db::getInstance()->execute('
CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'MyCustomModule` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`customer_id` int(255) NOT NULL,
`MyCustomModule` int(255) DEFAULT NULL,
`lastcheck_date` date,
`add_date` date,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
');
return parent::install() && $this->registerHook('Statusbtnoncustomerview');
}
/**
* Uninstall this module and remove it from all hooks
*
* #return bool
*/
public function uninstall()
{
return parent::uninstall() && $this->uninstallDb() && $this->unregisterHook('Statusbtnoncustomerview');
}
public function uninstallDb()
{
return Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'MyCustomModule');
}
public function hookStatusbtnoncustomerview()
{
/**
* Verify if this module is enabled
*/
if (!$this->active) {
return;
}
return $this->fetch('module:MyCustomModule/views/templates/hook/personal_information.html.twig');
}
/**
* Returns a string containing the HTML necessary to
* generate a configuration screen on the admin
*
* #return string
*/
public function getContent()
{
$output = null;
if (Tools::isSubmit('submit'.$this->name)) {
// get configuration fields value
$MyCustomModule_Account_Data = strval(Tools::getValue('MyCustomModule_Account_Data'));
$credit_Checkbox = strval(Tools::getValue('credit_Checkbox_1'));
$interval_Month = strval(Tools::getValue('Interval_Month'));
if (
!$MyCustomModule_Account_Data ||
empty($MyCustomModule_Account_Data) ||
!Validate::isGenericName($MyCustomModule_Account_Data)
) {
$output .= $this->displayError($this->l('Please Enter MyCustomModule Account Data.'));
} else{
// Update configuration fields value
Configuration::updateValue('MyCustomModule_Account_Data', $MyCustomModule_Account_Data);
Configuration::updateValue('credit_Checkbox_1', $credit_Checkbox);
Configuration::updateValue('Interval_Month', $interval_Month);
// Display message after successfully submit value
$output .= $this->displayConfirmation($this->l('Settings updated'));
}
}
return $output.$this->displayForm();
}
/**
* Display a form
*
* #param array $params
* #return form html using helper form
*/
public function displayForm()
{
// Get default language
$defaultLang = (int)Configuration::get('PS_LANG_DEFAULT');
$credit_Checkbox = [
[
'id'=>1,
'name'=>'',
'val' => 1
]
];
// Init Fields form array
$fieldsForm[0]['form'] = [
'legend' => [
'title' => $this->l('Configuration'),
],
'input' => [
[
'type' => 'text',
'label' => $this->l('MyCustomModule Account Data'),
'name' => 'MyCustomModule_Account_Data',
'required' => true
],
[
'type'=>'checkbox',
'label'=> $this->l('credit'),
'name'=>'credit_Checkbox',
'values'=>[
'query'=>$credit_Checkbox,
'id'=>'id',
'name'=>'name'
]
],
[
'type' => 'html',
'html_content' => '<input type="number" min="0" step="1" value="'.Configuration::get('Interval_Month').'" name="Interval_Month">',
'label' => $this->l('interval Month'),
'name' => 'Interval_Month',
'size' => 20
],
],
'submit' => [
'title' => $this->l('Save'),
'class' => 'btn btn-default pull-right'
]
];
$helper = new HelperForm();
// Module, token and currentIndex
$helper->module = $this;
$helper->name_controller = $this->name;
$helper->token = Tools::getAdminTokenLite('AdminModules');
$helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name;
// Language
$helper->default_form_language = $defaultLang;
$helper->allow_employee_form_lang = $defaultLang;
// Title and toolbar
$helper->title = $this->displayName;
$helper->show_toolbar = true; // false -> remove toolbar
$helper->toolbar_scroll = true; // yes - > Toolbar is always visible on the top of the screen.
$helper->submit_action = 'submit'.$this->name;
$helper->toolbar_btn = [
'save' => [
'desc' => $this->l('Save'),
'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name.
'&token='.Tools::getAdminTokenLite('AdminModules'),
],
'back' => [
'href' => AdminController::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminModules'),
'desc' => $this->l('Back to list')
]
];
// Load current value
$helper->fields_value['MyCustomModule_Account_Data'] = Configuration::get('MyCustomModule_Account_Data');
$helper->fields_value['credit_Checkbox_1'] = Configuration::get('credit_Checkbox_1');
$helper->fields_value['Interval_Month'] = Configuration::get('Interval_Month');
return $helper->generateForm($fieldsForm);
}
}
i trying this url : http://localhost/prestashop/admin482vzxnel/index.php?module=mymodule&controller=checkstatus
geting error :
Page not found
The controller checkstatus is missing or invalid.
Thanks
To include a Class in a module you can use php require. Once included in module main file you can create an instance of you class where you want in your module.
To assign variable that you can use on template there is $this->context->smarty->assig() function.
The code Below is just an example of how include and use a Class in a custom module Controller.
Even the edit in the hookStatusbtnoncustomerview() is an example of how retrieve the module link and the link on his method, so take the code below such as it is, only an example
require_once (dirname(__FILE__)).'contollers/admin/Checkstatus.php';
class MyModule extends PaymentModule
{
public function __construct()
{
$this->name = 'MyCustomModule';
$this->tab = 'payments XYZ';
$this->version = '1.0';
$this->author = 'XYZ Technologies';
$this->bootstrap = true;
$this->displayName = 'XYZ';
$this->description = 'XYZ.';
$this->confirmUninstall = 'Are you sure you want to uninstall XYZ module?';
$this->ps_versions_compliancy = array('min' => '1.7.0', 'max' => _PS_VERSION_);
$this->allow_countries = array('CH', 'LI', 'AT', 'DE');
$this->allow_currencies = array('CHF', 'EUR');
$this->checkController = null;
parent::__construct();
}
private function _useCheckstatus(){
$this->checkController = new CheckstatusController();
}
public function MyMethod (){
$this->_useCheckstatus();
$this->checkController->demoAction();
}
public function hookStatusbtnoncustomerview()
{
/**
* Verify if this module is enabled
*/
if (!$this->active) {
return;
}
$this->context->smarty->assign(
array(
'my_module_name' => Configuration::get('MyCustomModule'),
'my_module_link' => $this->context->link->getModuleLink('MyCustomModule', 'mymethod'),
'token' => Tools::getToken(false)
)
);
return $this->fetch('module:MyCustomModule/views/templates/hook/personal_information.html.twig');
}
.........
it's not easy to investigate how prestashop works, so my tip is: look at the code from other modules, and take example from them.
Instead if you need to override a Core Prestashop Controller in your module you have to put the override file in /mymodule/override/controllers/admin/ModuleAdminController.php. installing/resetting the module will put this file within root/override/controllers/admin/ folder and the override becomes active. just remember to clear PS cache in "advanced settings->performance"
I'm currently working on a project using the Phalcon Framework that has pages with complex forms and a lot of inputs, to break it down nicely I'm dividing the forms into a step-by-step process.
How would one validate the form on each step before going to the next step and then save the whole form on the final step?
I can't seem to find anything documented about this sort of process as it likes to validate the form in it's entirety if I use the form builder.
Simple, just create a custom methods in your form class to validate any step, and the posted data from some step save into message class and store it into session by "stepX", when posted data is not valid just set defaults from post. When valid save it into session as i describe above.
For example how i mean "controller"
<?php
class MyController extends BaseController {
public function processStep1Action(){
$form = new MyForm();
if($this->request->isPost()){//im using my custom request class
if(!$form->isValid($this->request->getPost()){
//error messages goes here
$form->setDefaultsFromRequest($this->request); // it will set the filled data
}
else {
$messageClass = new MyMessageContainer();
$messageClass->setData($this->request);//inside parse requested data into message class, or parse it as $messageClass->name = $this->request->getPost('name');
$this->session->save('step1',$messageClass); //maybe it would be want to serialize it
//then redirect to the step 2 or x
}
}
}
}
So in the next step you can access data from sessions $this->session->get('step1'); so you can in final step load all posted data and store it into DB.
I hope this helps! :)
here is my form maybe it can be helpful for you.
<?php
namespace Manager\Library\Forms\User;
use Phalcon\Forms\Form,
Phalcon\Forms\Element\Email,
Phalcon\Forms\Element\Select,
Phalcon\Forms\Element\Password,
Phalcon\Forms\Element\Check,
Phalcon\Validation\Validator\Confirmation,
Phalcon\Validation\Validator\StringLength,
Phalcon\Forms\Element\Submit,
Phalcon\Validation\Validator\PresenceOf,
Model\Group;
class AddUser extends Form {
public function initialize()
{
$email = new Email('email');
$email->addValidators(array(
new \Phalcon\Validation\Validator\Email(array(
'message' => 'Nezadali jste email nebo má nesprávny tvar(email#domena.tld).'
))
));
$this->add($email);
$this->initGroupElement();
$password = new Password('password');
$password
->addValidator(new StringLength(array('min' => 6,'messageMinimum' => 'Nezadali jste heslo nebo je příliš krátke, minimální počet znaků je 6.')))
->addValidator(new Confirmation(array('with' => 'password-again',"message" => "Zadané hesla se neshodují.")));
$this->add($password);
$repeatPassword = new Password('password-again');
$this->add($repeatPassword);
$this->initializeProfileElements();
$active = new Check('active',array('value' => 1));
$this->add($active);
$this->add( new Submit('save') );
\Phalcon\Tag::setDefault('password', '');
\Phalcon\Tag::setDefault('password-again', '');
}
public function initializeEdit(){
$email = new Email('email');
$email->addValidators(array(
new \Phalcon\Validation\Validator\Email(array(
'message' => 'Nezadali jste email nebo má nesprávny tvar(email#domena.tld).'
))
));
$this->add($email);
$this->initGroupElement();
$password = new Password('password');
$this->add($password);
$repeatPassword = new Password('password-again');
$this->add($repeatPassword);
$this->initializeProfileElements();
$active = new Check('active',array('value' => 1));
$this->add($active);
$this->add( new Submit('save') );
\Phalcon\Tag::setDefault('password', '');
\Phalcon\Tag::setDefault('password-again', '');
}
protected function initGroupElement(){
$auth = \Core\Auth::getIdentity();
$groups = new Group();
// $groups->addColumns(array('id','name'));
//set global condition about Super Admin
$groups->addFilter('id', 1,'<>');
if($auth){
//set restrictions for main groups
if((int)$auth->group_id === 1){ //super admingroup
//no filter
}
else if((int)$auth->group_id === 2){ //admin group
$groups->addFilter('id', 1,'>');
}
else if((int)$auth->group_id === 6){//Provozovatel group
$groups->addFilter('id',array(3,6,7));
$groups->addFilter('public', 1,'=',true);
}
else { // other groups
$groups->addFilter('public', 1);
}
}
$groups = $groups->findFiltered();
$groupElement = new Select('group');
foreach($groups as $group){
$groupElement->addOption(array($group->id => $group->name));
}
$this->add($groupElement);
}
protected function initializeProfileElements(){
$forename = new \Phalcon\Forms\Element\Text('forename');
$this->add($forename);
$surname = new \Phalcon\Forms\Element\Text('surname');
$this->add($surname);
$street = new \Phalcon\Forms\Element\Text('street');
$this->add($street);
$postal = new \Phalcon\Forms\Element\Text('postal');
$this->add($postal);
$city = new \Phalcon\Forms\Element\Text('city');
$this->add($city);
$ic = new \Phalcon\Forms\Element\Text('ic');
$this->add($ic);
$dic = new \Phalcon\Forms\Element\Text('dic');
$this->add($dic);
}
public function setDefault($fieldName,$value){
\Phalcon\Tag::setDefault($fieldName, $value);
}
public function setDefaults($object){
if($object instanceof \Model\User){
$this->setDefaultsFromObject($object);
}
else if($object instanceof \Phalcon\Http\Request){
$this->setDefaultsFromRequest($object);
}
}
protected function setDefaultsFromObject(\Model\User $user){
$profile = $user->getRelated('\Model\Profile');
\Phalcon\Tag::setDefaults(array(
'email' => $user->email,
'group' => $user->group_id,
'active' => $user->active,
'forename' => $profile->forename,
'surname' => $profile->surname,
'street' => $profile->street,
'city' => $profile->city,
'postal' => $profile->postal,
'ic' => $profile->IC,
'dic' => $profile->DIC
));
}
protected function setDefaultsFromRequest(\Phalcon\Http\Request $request){
\Phalcon\Tag::setDefaults(array(
'email' => $request->getPost('email'),
'group' => $request->getPost('group'),
'active' => $request->getPost('active')
));
\Phalcon\Tag::setDefaults(array(
'forename' => $request->getPost('forename'),
'surname' => $request->getPost('surname'),
'street' => $request->getPost('street'),
'city' => $request->getPost('city'),
'postal' => $request->getPost('postal'),
'ic' => $request->getPost('ic'),
'dic' => $request->getPost('dic')
));
}
}
In addition to Kamil's answer, another option to consider is to use Javascript on the front-end to handle your multi-step form. This will add some complexity as you will need to have the javascript to handle the form steps and do preliminary validation, but it only requires a single submit where you can validate content within a single method.
I've created a module to authenticate a user. Now, after login I go to the index action and the system tells me that the authentication is all working fine. But What I want is to print some more user details from the Users table. When I try:
print_r($this->getServiceLocator()->get('AuthService')->getAdapter()->getResultRowObject());
I get no result. What am I doing wrong?
Thanks for your help.
In my module.php I've the following code(snippet):
public function getServiceConfig()
{
return array(
'abstract_factories' => array(),
'aliases' => array(),
'factories' => array(
// Some more code here but removed for simplicity
// Autentication
'AuthService' => function ($sm) {
$adapter = $sm->get('master_db');
$dbAuthAdapter = new DbAuthAdapter ( $adapter, 'Users', 'email', 'password' );
$auth = new AuthenticationService();
$auth->setAdapter ( $dbAuthAdapter );
return $auth;
},
// Some more code here but removed for simplicity
}
In my IndexController.php I've the following (snippets):
public function indexAction()
{
if(!$this->getServiceLocator()->get('AuthService')->hasIdentity()){
return $this->redirect()->toUrl('login');
}
echo "hello, it works!";
exit;
}
public function loginAction(){
$form = $this->getServiceLocator()->get('LoginForm');
$viewModel = new ViewModel(array('form' =>
$form));
return $viewModel;
}
public function processAction(){
// Lots of code here
if($bcrypt->verify($loginData['password'], $userData->password))
{
$this->getAuthService()
->getAdapter()
->setIdentity($loginData['email'])
->setCredential($userData->password);
$result = $this->getAuthService()->authenticate();
}
// Lots of code here where I check if $result->isValid and route to the
// correct action
}
public function getAuthService() {
if(!isset($this->authservice)) {
$this->authservice = $this->getServiceLocator()->get('AuthService');
}
return $this->authservice;
}
Instead of refering to the authentication result object (which properly only exists in the authentication request) you can simply store user details in the authentication identity (#see http://framework.zend.com/manual/2.1/en/modules/zend.authentication.intro.html).
For your case you could also store user specific details right after the validation of the authentication result in the authentication storage:
if ($result->isValid()) {
//authentication success
$resultRow = $this->authService->getAdapter()->getResultRowObject();
$this->authService->getStorage()->write(array(
'id' => $resultRow->id,
'user_agent' => $request->getServer('HTTP_USER_AGENT'))
);
}
(This information was taken from this authentication tutorial http://samsonasik.wordpress.com/2013/05/29/zend-framework-2-working-with-authenticationservice-and-db-session-save-handler/)
I am looking for a way to access and change the DATABASE_CONFIG variables, based on user input. Using CakePHP I created a custom datasource, based on the one provided in the docs, to access an external API. The API returns a JSON string containing the 12 most recent objects. I need to be able to change the page number in the API request to get the next 12 results, as well as accept a free text query entered by the user.
app/Config/Database.php
class DATABASE_CONFIG {
public $behance = array(
'datasource' => 'BehanceDatasource',
'api_key' => '123456789',
'page' => '1',
'text_query' => 'foo'
);
}
app/Model/Datasource/BehanceDataSource.php
App::uses('HttpSocket', 'Network/Http');
class BehanceDatasource extends DataSource {
public $description = 'Beehance datasource';
public $config = array(
'api_key' => '',
'page' => '',
'text_query' => ''
);
public function __construct($config) {
parent::__construct($config);
$this->Http = new HttpSocket();
}
public function listSources($data = null) {
return null;
}
public function describe($model) {
return $this->_schema;
}
public function calculate(Model $model, $func, $params = array()) {
return 'COUNT';
}
public function read(Model $model, $queryData = array(), $recursive = null) {
if ($queryData['fields'] === 'COUNT') {
return array(array(array('count' => 1)));
}
$queryData['conditions']['api_key'] = $this->config['api_key'];
$queryData['conditions']['page'] = $this->config['page'];
$queryData['conditions']['page'] = $this->config['text_query'];
$json = $this->Http->get('http://www.behance.net/v2/projects', $queryData['conditions']);
$res = json_decode($json, true);
if (is_null($res)) {
$error = json_last_error();
throw new CakeException($error);
}
return array($model->alias => $res);
}
}
Is there anyway to access and change the $behance array, or is there another way to go about accessing an external API with cakePHP that I am totally missing?
So far I have been unable to successfully implement ACLs (permissions) in SabreDAV.
I have implemented SabreDAV in Code Igniter with my own Auth, Principal and CalDAV backend. This the actual code from the controller:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class CalDAV extends CI_Controller {
public function _remap() {
$this->load->library('SabreDAV');
$authBackend = new SabreDAV_DAV_Auth_Backend_Tank_Auth;
$principalBackend = new Sabre_DAVACL_PrincipalBackend_Click4Time;
$calendarBackend = new Sabre_CalDAV_Backend_Click4Time;
// Directory tree
$tree = array(
new Sabre_DAVACL_PrincipalCollection($principalBackend),
new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend)
);
// The object tree needs in turn to be passed to the server class
$server = new Sabre_DAV_Server($tree);
// You are highly encouraged to set your WebDAV server base url. Without it,
// SabreDAV will guess, but the guess is not always correct. Putting the
// server on the root of the domain will improve compatibility.
$server->setBaseUri('/caldav/');
// Authentication plugin
$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend, 'SabreDAV');
$server->addPlugin($authPlugin);
// CalDAV plugin
$caldavPlugin = new Sabre_CalDAV_Plugin();
$server->addPlugin($caldavPlugin);
// ACL plugin
$aclPlugin = new Sabre_DAVACL_Custom;
$server->addPlugin($aclPlugin);
// Support for html frontend
$browser = new Sabre_DAV_Browser_Plugin();
$server->addPlugin($browser);
$server->exec();
}
}
My current attempt at implementing permissions has been through my custom ACL Plugin:
<?php
class Sabre_DAVACL_Custom extends Sabre_DAVACL_Plugin {
public $allowAccessToNodesWithoutACL = false;
private function _getCurrentUserName() {
$authPlugin = $this->server->getPlugin('auth');
if (is_null($authPlugin)) return null;
return $authPlugin->getCurrentUser();
}
public function getACL($node) {
$user = $this->_getCurrentUserName();
$path = $node->getName();
if ($path == 'calendars' || $path == 'principals' || $path == 'root') {
return array(
array(
'privilege' => '{DAV:}read',
'principal' => 'principals/' . $user,
'protected' => true,
)
);
}
else if ($path == 'calendars/' . $user) {
return array(
array(
'privilege' => '{DAV:}read',
'principal' => 'principals/' . $user,
'protected' => true,
)
);
}
return array();
}
}
This code pretty much works except the second check which should authorize the user to see his or her own calendar(s). I am unable to get the full path name for $node.
This may be the wrong way to implement but I have been unable to find any documentation to confirm that this is the way to implement ACLs.
i'm using a different attempt, i extended the plugin, just like you did but then i replaced getSupportedPrivilegeSet($node) instead.
in sabredav 1.8.6 it looks like this:
public function getSupportedPrivilegeSet($node) {
if (is_string($node)) {
$node = $this->server->tree->getNodeForPath($node);
}
if ($node instanceof IACL) {
$result = $node->getSupportedPrivilegeSet();
if ($result)
return $result;
}
return self::getDefaultSupportedPrivilegeSet();
}
now you can use the classes instead of the path which i found more usefull, i.e.:
class DavCalAcl extends \Sabre\DAVACL\Plugin {
public function getSupportedPrivilegeSet($node) {
if (is_string($node)) {
$node = $this->server->tree->getNodeForPath($node);
}
if($node instanceof \Sabre\CalDAV\Calendar || $node instanceof \Sabre\CalDAV\CalendarObject) {
return array(
array(
'privilege' => '{DAV:}read',
'aggregates' => array(
array(
'privilege' => '{DAV:}read-acl',
'abstract' => true,
),
array(
'privilege' => '{DAV:}read-current-user-privilege-set',
'abstract' => true,
),
),
)
);
}
if ($node instanceof \Sabre\DAVACL\IACL) {
$result = $node->getSupportedPrivilegeSet();
if ($result)
return $result;
}
return self::getDefaultSupportedPrivilegeSet();
}
}
this is my current attempt to get iCal to recognize a calendar as read-only... i'm not quite there yet but maybe this will help you in better identifying the objects
if you want the absolute path of a node i guess you could always go to the root search it for your current node and by doing so recording the path which took you there. as far as i checked the nodes in sabredav do not support a parent or a root property.
[UPDATE]
the best way seems to be to override getACL in the plugin. here you can test for the node's class and return what you really want on instead of the stuff which is returned by the default objects (for instance look at UserCalendars->getACL().
here's my working solution for read-only enforcement based on the object types:
class DavCalAcl extends \Sabre\DAVACL\Plugin {
/**
* Returns the full ACL list.
*
* Either a uri or a DAV\INode may be passed.
*
* null will be returned if the node doesn't support ACLs.
*
* #param string|DAV\INode $node
* #return array
*/
public function getACL($node) {
if (is_string($node)) {
$node = $this->server->tree->getNodeForPath($node);
}
if (!$node instanceof \Sabre\DAVACL\IACL) {
return null;
}
if( $node instanceof \Sabre\CalDAV\Calendar ||
$node instanceof \Sabre\CalDAV\CalendarObject ||
$node instanceof \Sabre\CalDAV\UserCalendars
) {
$acl = array(
array(
'privilege' => '{DAV:}read',
'principal' => $node->getOwner(),
'protected' => true,
),
);
} else {
$acl = $node->getACL();
}
foreach($this->adminPrincipals as $adminPrincipal) {
$acl[] = array(
'principal' => $adminPrincipal,
'privilege' => '{DAV:}all',
'protected' => true,
);
}
return $acl;
}
}