Today I want to present some useful shell scripts I often use.
Hopefully you find them useful, as well 🙂
Where to put them
Drop shells in a vendor folder of your choice (either app, core, or plugin), for instance:
/app/vendors/shells/
or like me /app/plugins/tools/vendors/shells/
Password Reset
A quick and easy shell script to reset all passwords for local development.
Usage:
cake pwd_reset [pwd]
The password is optional (will be prompted otherwise)
The code:
<?php
# enhancement for plugin user model
if (!defined('CLASS_USER')) {
define('CLASS_USER', 'User');
}
/**
* reset user passwords
*/
class PwdResetShell extends Shell {
var $tasks = array();
//var $uses = array('User');
var $Auth = null;
/**
* reset all pwds to a simply pwd (for local development)
* 2011-08-01 ms
*/
function main() {
$components = array('AuthExt', 'Auth');
foreach ($components as $component) {
if (App::import('Component', $component)) {
$component .='Component';
$this->Auth = new $component();
break;
}
}
if (!is_object($this->Auth)) {
$this->out('No Auth Component found');
die();
}
$this->out('Using: '.get_class($this->Auth).' (Abort with STRG+C)');
if (!empty($this->args[0]) && mb_strlen($this->args[0]) >= 2) {
$pwToHash = $this->args[0];
}
while (empty($pwToHash) || mb_strlen($pwToHash) < 2) {
$pwToHash = $this->in(__('Password to Hash (2 characters at least)', true));
}
$this->hr();
$this->out('pwd:');
$this->out($pwToHash);
$pw = $this->Auth->password($pwToHash);
$this->hr();
$this->out('hash:');
$this->out($pw);
$this->hr();
$this->out('resetting...');
$this->User = ClassRegistry::init(CLASS_USER);
if (!$this->User->hasField('password')) {
$this->error(CLASS_USER.' model doesnt have a password field!');
}
if (method_exists($this->User, 'escapeValue')) {
$newPwd = $this->User->escapeValue($pw);
} else {
$newPwd = '\''.$pw.'\'';
}
$this->User->recursive = -1;
$this->User->updateAll(array('password'=>$newPwd), array('password !='=>$pw));
$count = $this->User->getAffectedRows();
$this->out($count.' pwds resetted - DONE');
}
function help() {
$this->out('-- Hash and Reset all user passwords with Auth(Ext) Component --');
}
}
Useful if you just switched the salt, the hash method or imported users from another DB for testing 🙂
New User
How do you set up an admin account with a new project if you can’t login? Sure, allow(*) or other hacks.
But wouldnt a simple command like cake user
be nicer? Check it out:
<?php
if (!defined('CLASS_USER')) {
define('CLASS_USER', 'User');
}
class UserShell extends Shell {
var $tasks = array();
var $uses = array(CLASS_USER);
function help() {
$this->out('command: cake user');
}
//TODO: refactor (smaller sub-parts)
function main() {
if (App::import('Component', 'AuthExt')) {
$this->Auth = new AuthExtComponent();
} else {
App::import('Component', 'Auth');
$this->Auth = new AuthComponent();
}
while (empty($username)) {
$username = $this->in(__('Username (2 characters at least)', true));
}
while (empty($password)) {
$password = $this->in(__('Password (2 characters at least)', true));
}
$schema = $this->User->schema();
if (isset($this->User->Role) && is_object($this->User->Role)) {
$roles = $this->User->Role->find('list');
if (!empty($roles)) {
$this->out('');
pr($roles);
}
$roleIds = array_keys($roles);
while (!empty($roles) && empty($role)) {
$role = $this->in(__('Role', true), $roleIds);
}
} elseif (method_exists($this->User, 'roles')) {
$roles = User::roles();
if (!empty($roles)) {
$this->out('');
pr ($roles);
}
$roleIds = array_keys($roles);
while (!empty($roles) && empty($role)) {
$role = $this->in(__('Role', true), $roleIds);
}
}
if (empty($roles)) {
$this->out('No Role found (either no table, or no data)');
$role = $this->in(__('Please insert a role manually', true));
}
$this->out('');
$pwd = $this->Auth->password($password);
$data = array('User'=>array(
'password' => $pwd,
'active' => 1
));
if (!empty($username)) {
$data['User']['username'] = $username;
}
if (!empty($email)) {
$data['User']['email'] = $email;
}
if (!empty($role)) {
$data['User']['role_id'] = $role;
}
if (!empty($schema['status']) && method_exists('User', 'statuses')) {
$statuses = User::statuses();
pr($statuses);
while(empty($status)) {
$status = $this->in(__('Please insert a status', true), array_keys($statuses));
}
$data['User']['status'] = $status;
}
if (!empty($schema['email'])) {
$provideEmail = $this->in(__('Provide Email? ', true),array('y', 'n'), 'n');
if ($provideEmail === 'y') {
$email = $this->in(__('Please insert an email', true));
$data['User']['email'] = $email;
}
if (!empty($schema['email_confirmed'])) {
$data['User']['email_confirmed'] = 1;
}
}
$this->out('');
pr ($data);
$this->out('');
$this->out('');
$continue = $this->in(__('Continue? ', true),array('y', 'n'), 'n');
if ($continue != 'y') {
$this->error('Not Executed!');
}
$this->out('');
$this->hr();
if ($this->User->save($data)) {
$this->out('User inserted! ID: '.$this->User->id);
} else {
$this->error('User could not be inserted ('.print_r($this->User->validationErrors, true).')');
}
}
}
Remove the closing PHP tags
Since CakePHP1.3 the closing tags are omitted – for a good reason. You should also make sure your app does not contain any of those closing tags in PHP files. With this shell it’s done in seconds:
<?php
/**
* removes closing php tag (?>) from php files
* it also makes sure there is no whitespace at the beginning of the file
*/
class PhpTagShell extends Shell {
var $tasks = array();
var $uses = array();
var $autoCorrectAll = false;
# each report: [0] => found, [1] => corrected
var $report = array('leading'=>array(0, 0),'trailing'=>array(0, 0));
function main() {
if(isset($this->args[0]) && !empty($this->args[0])) {
$folder = realpath($this->args[0]);
} else {
$folder = APP;
}
if(is_file($folder)) {
$r = array($folder);
} else {
App::import('Core',array('Folder'));
$App = new Folder($folder);
$this->out("Find recursive *.php in [".$folder."] ....");
$r = $App->findRecursive('.*\.php');
}
$folders = array();
foreach($r as $file) {
$error = array();
$action = '';
$c = file_get_contents($file);
if(preg_match('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', $c)) {
$error[] = 'leading';
}
if(preg_match('/\?\>[\n\r|\n\r|\n|\r|\s]*$/', $c)) {
$error[] = 'trailing';
}
if (!empty($error)) {
foreach($error as $e) {
$this->report[$e][0]++;
}
$this->out('');
$this->out('contains '.rtrim(implode($error, ', '), ', ').' whitespaces / php tags: '.$this->shortPath($file));
if (!$this->autoCorrectAll) {
$dirname = dirname($file);
if (in_array($dirname, $folders)) {
$action = 'y';
}
while (empty($action)) {
//TODO: [r]!
$action = $this->in(__('Remove? [y]/[n], [a] for all in this folder, [r] for all below, [*] for all files(!), [q] to quit', true), array('y','n','r','a','q','*'), 'q');
}
} else {
$action = 'y';
}
if ($action == '*') {
$action = 'y';
$this->autoCorrectAll = true;
} elseif ($action == 'a') {
$action = 'y';
$folders[] = $dirname;
$this->out('All: '.$dirname);
}
if($action == 'q') {
die('Abort... Done');
} elseif ($action == 'y') {
$res = $c;
if(in_array('leading', $error)) {
$res = preg_replace('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', '<?php', $res);
}
if(in_array('trailing', $error)) {
$res = preg_replace('/\?\>[\n\r|\n\r|\n|\r|\s]*$/', "\n", $res);
}
file_put_contents($file, $res);
foreach($error as $e) {
$this->report[$e][1]++;
$this->out('fixed '.$e.' php tag: '.$this->shortPath($file));
}
}
}
}
# report
$this->out('--------');
$this->out('found '.$this->report['leading'][0].' leading, '.$this->report['trailing'][0].' trailing ws / php tag');
$this->out('fixed '.$this->report['leading'][1].' leading, '.$this->report['trailing'][1].' trailing ws / php tag');
}
}
You an either go through the complete app. Or you can pass a specific path like so:
cake php_tag C:\testfolder
(custom folder) or cake php_tag config
(/app/config).
UPDATE 2013-02-12 for 2.x
I put everything in my Tools plugin for easier use. It also contains the most recent (bug)fixes. Make sure you use those files instead.
And the path would now be APP/Console/Command/
– but it is better to use the Tools plugin as a whole.
Just call them as
cake Tools.ShellName command
now.
Good work very useful 🙂 thanks
Thanks for sharing. I love your site and CakePHP