Jump to content
Larry Ullman's Book Forums

Ziggi

Members
  • Posts

    56
  • Joined

  • Last visited

  • Days Won

    1

Everything posted by Ziggi

  1. Hi, You know what... human stupidity has no limits actually. My solution works perfectly. The "drop-down" issue I have mentioned was... an artifact: The drop-down item you can see on the above screen shot is not Yii feature! It is... A Firefox form autocomplete. No wonder I could not get this working tweaking my application... I feel like an idiot. The only advantage is I finally realized that :-) Regards,
  2. Hmm, As far as I can see you should rather try using GET method in your code rather than POST: $model=User::model()->findByPk($_GET['id']); etc.
  3. Hi Antonio, It's all about User model. There is no special 'Search' model but there are search criteria within a User model class. This is my Users.php model: <?php /** * This is the model class for table "users". */ class Users extends CActiveRecord { public $fullname; public static function model($className=__CLASS__) { return parent::model($className); } public function tableName() { return 'users'; } public function rules() { return array( array('login, pass_hash, surname, firstname', 'filter', 'filter' => 'trim'), array('login, pass_hash, surname, firstname', 'filter', 'filter' => 'strip_tags'), array('id', 'numerical', 'integerOnly'=>true, 'min'=>0, 'except'=>'insert'), array('login', 'required', 'on'=>'insert'), array('login', 'length', 'encoding'=>'utf8', 'min'=>5, 'max'=>16), array('login', 'match', 'pattern'=>'/^[a-z]{1}[a-z0-9-_]*$/'), array('login', 'unique', 'caseSensitive'=>FALSE), array('pass_hash', 'required', 'on'=>'insert'), array('pass_hash', 'length', 'is'=>32), array('surname, firstname, email, group, active', 'required'), array('surname, firstname', 'length', 'encoding'=>'utf8', 'min'=>2, 'max'=>32), array('surname, firstname', 'match', 'pattern'=>'/^[A-Z]{1}[a-zA-Z- ]*$/'), array('email', 'length', 'max'=>64), array('email', 'email', 'checkMX'=>TRUE), array('group', 'in', 'range'=>array('account','designer','customer','admin')), array('active', 'boolean', 'strict'=>TRUE), array('fullname', 'safe', 'on'=>'search'), ); } public function attributeLabels() { return array( 'login' => 'Login', 'pass_hash' => 'Pass', 'surname' => 'Family name', 'firstname' => 'Given name', 'email' => 'E-mail', 'group' => 'Role', 'active' => 'Is active?', 'fullname' => 'Name', ); } public function search() { $criteria=new CDbCriteria; $criteria->select = array('*', 'CONCAT(surname, ", ", firstname) AS fullname'); $criteria->compare('id',$this->id,true); $criteria->compare('login',$this->login,true); $criteria->compare('CONCAT(surname, ", ", firstname)',$this->fullname,true); $criteria->compare('group',$this->group,true); $criteria->compare('active',$this->active); return new CActiveDataProvider($this, array( 'criteria'=>$criteria, )); } } This is the only way actually working - excluding javascript 'drop down' seach suggesions on filter in case of 'fullname' one. I really tried it starting from plain virtual attribute but this do not goes into search dataprovider. According my findings - the configuration I have just provided above is the only one really proven working. Virtual attribute getter is not applicable in this context. So now the only problem is this misfortune search suggestion drop-down on filter and I consider this minor problem. But if you have any idea on that - I would appreciate.
  4. Hmm, This actually works but... javascript search suggestions do not show up while typing into 'fullname' field. Looks like look-up while typing has issues with such a complex stuff.
  5. :-) Yeap - that was too easy: $criteria->compare('CONCAT(surname, ", ", firstname)',$this->fullname,true); I can have a drink now ;-)
  6. Hi, This one is nasty. I did a research an actually found no relevant solution. The task is: I have a 'User' model with attributes: 'surname' and 'firstname' But on the user search form I would like to search by 'fullname', where 'fullname' is: public $fullname; public function getFullname(){ return $this->surname . ', ' . $this->firstname; } Well, I seems not that easy. First of all - there is no easy way to feed virtual attribute to search dataprovider. The only way is through computed column: $criteria=new CDbCriteria; $criteria->select = array('*', 'CONCAT(surname, ", ", firstname) AS fullname'); $criteria->compare('fullname', $this->fullname, true); This is good enough to feed 'fullname' to dataprovider but actual search fails with error: CDbCommand failed to execute the SQL statement: SQLSTATE[42S22]: Column not found 'fullname' No big surprise actually, but the problem persists and I am just curious what is the right way to solve it. If anybody can share some ideas I would appreciate. Rgs, Ziggi
  7. They do it the way you guessed: protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from=null) { if (!empty($this->action_function) && is_callable($this->action_function)) { $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); call_user_func_array($this->action_function, $params); } } The helper function is the right "Yii-istic" way to go - thank you once again, Antonio. BTW - did you study IT on the university or you've learnt it all by your own? I would say... I like the way you explain - it is more about "how to understand the code and solve the problem" rather than "how to solve the problem only". I am missing academic background and too often get lost in various scenarios...
  8. Hi, There is a new version of PHPMailer released (v. 5.2.2). It seems this new version is working well with MetYii's wrapper but I can't figure out how to use a great new PHPMailer 'callback' feature. PHPMailer documents it only by example: /* Callback (action) function * bool $result result of the send action * string $to email address of the recipient * string $cc cc email addresses * string $bcc bcc email addresses * string $subject the subject * string $body the email body * @return boolean */ require_once '../class.phpmailer.php'; $mail = new PHPMailer(); function callbackAction ($result, $to, $cc, $bcc, $subject, $body) { /* this callback example echos the results to the screen - implement to post to databases, build CSV log files, etc., with minor changes */ $to = cleanEmails($to,'to'); $cc = cleanEmails($cc[0],'cc'); $bcc = cleanEmails($bcc[0],'cc'); echo $result . "\tTo: " . $to['Name'] . "\tTo: " . $to['Email'] . "\tCc: " . $cc['Name'] . "\tCc: " . $cc['Email'] . "\tBcc: " . $bcc['Name'] . "\tBcc: " . $bcc['Email'] . "\t" . $subject . "<br />\n"; return true; } try { $mail->IsMail(); // telling the class to use SMTP $mail->SetFrom('you@yourdomain.com', 'Your Name'); $mail->AddAddress('another@yourdomain.com', 'John Doe'); $mail->Subject = 'PHPMailer Lite Test Subject via Mail()'; $mail->AltBody = 'To view the message, please use an HTML compatible email viewer!'; // optional - MsgHTML will create an alternate automatically $mail->MsgHTML(file_get_contents('contents.html')); $mail->AddAttachment('images/phpmailer.gif'); // attachment $mail->AddAttachment('images/phpmailer_mini.gif'); // attachment $mail->action_function = 'callbackAction'; $mail->Send(); echo "Message Sent OK</p>\n"; } catch (phpmailerException $e) { echo $e->errorMessage(); //Pretty error messages from PHPMailer } catch (Exception $e) { echo $e->getMessage(); //Boring error messages from anything else! } You can see a callback function and e-mail sending block. When using a wrapper, sending e-mail looks quite similar: Yii::app()->mailer->IsSMTP(); try{ Yii::app()->mailer->Host = Yii::app()->params['mail']['smtp_host']; Yii::app()->mailer->Encoding = Yii::app()->params['mail']['smtp_encoding']; Yii::app()->mailer->CharSet = Yii::app()->params['mail']['smtp_charset']; Yii::app()->mailer->WordWrap = Yii::app()->params['mail']['smtp_word_wrap']; Yii::app()->mailer->From = $model->email; Yii::app()->mailer->FromName = $name; Yii::app()->mailer->AddReplyTo($model->email); Yii::app()->mailer->AddAddress(Yii::app()->params['adminEmail']); Yii::app()->mailer->Subject = $subject; Yii::app()->mailer->getView('contact_form', array('name'=>$model->name, 'email'=>$model->email, 'subject'=>$model->subject, 'body'=>$body), 'main'); Yii::app()->mailer->action_function = 'mailerCallback'; Yii::app()->mailer->Send(); }catch(phpmailerException $e){ Yii::app()->user->setFlash('contact', $e->errorMessage()); }catch(Exception $e){ Yii::app()->user->setFlash('contact', $e->getMessage()); } $this->refresh(); } You may have notice I already added: Yii::app()->mailer->action_function = 'mailerCallback'; but... I have no idea where to put the 'mailerCallback' function within Yii application context. I tried to add it to controller class but it does not work this way. Can you help me please? Rgs, Ziggi
  9. Thank you, Larry! Ffor clarification - this setup is for development only. While moved onto production server - symlinks are gone, the inner 'protected' folder is not needed any more and can be removed from production server. This way the only thing left is safe configuration with critical folders outside web root.
  10. Actually, I found symlinking directories very helpful while working with IDEs like PhpStorm what is my favorite due to its speed, versatility and reliability. Then you can create a following structure (dots are only for formatting): /-| | |-YiiFramework |-YourProjectName-| |..............................|-protected |-web-| |.........|-protected So now you can symlink both 'protected' folders keeping your IDE happy as well as following Larry's safety guidance.
  11. The real problem is documentation, where way of invoking is never mentioned and only through tiresome and tedious code reverse-engineerng one may learn for instance that controller's actions are actually created using reflection - so no wonder it's impossible to create new action dynamically (in runtime) using any "regular" measure. Though Yii documentaion is extraordinary complete as for open-source project standards - it lacks information about class invoking and delegates the task to personal experience of Yii users. Unfortunately, this style very often renders community input difficult to read by a novice users as though one my learn on various fora how to "extend" given class - he or she still remains in state of perfect confusion: "but how the hell shall I invoke this class to apply one of its great methods in my particular case???". Best regards, Ziggi
  12. Well, I've got it! Presuming these are global access rules defined in components/Controller.php: public function accessRules(){ return array( array( // these pages are accessible by all (AJAX captcha is included here) 'allow', 'actions'=>array('index', 'about'), 'users'=>array('*'), ), array( // these pages are for non-logged-in users only 'allow', 'actions'=>array(''), 'users'=>array('?'), ), array( // these pages are for loged-in users only 'allow', 'actions'=>array('manage', 'help'), 'users'=>array('@'), ), array( // black access to everything what was not explicite allowed 'deny', 'users'=>array('*'), ), ); } Presuming both 'about' and 'help' pages are static ones, I define the following StaticController to handle static pages: <?php class StaticController extends Controller { private $akcja; public function getAkcja() { return $this->akcja; } /** * This is the action to handle external exceptions. */ public function actionerror() { if($error=Yii::app()->errorHandler->error) { if(Yii::app()->request->isAjaxRequest) echo $error['message']; else $this->render('error', $error); } } /** * This is the action to render static files as missing actions. */ public function missingAction($akcja){ $this->akcja = $akcja; $views = $this->getViewPath(); if(file_exists($views . "/" . $akcja . ".php")){ $this->checkAccess(Yii::app()->user, $akcja); $this->render($akcja); }else{ throw new CHttpException(404,'Page not found!'); } } public function checkAccess($user, $akcja) { include_once(Yii::getFrameworkPath() . '/web/auth/CAccessControlFilter.php'); $fa = new FakeAction($akcja); $fc = CFilterChain::create($this, $fa, $this->filters()); $acf = new CAccessControlFilter(); $acf->filter($fc); } } class FakeAction { public function __construct($id) { $this->id = $id; } public function getId() { return $this->id; } public function runWithParams($param){ return true; } } This code contains minimalistic 'FakeAction' class. This class is used to generate a "dummy action" object what has only two methods neccessary to pass through the rest of the pipe. This is used to create filterChain object what is then supplied as an argument of the 'filter' method of AccessControlFilter object. In case of success (user is allowed to use given action) - ACFilter runs a supplied 'dummy' action what effectively does nothing and the process goes back to the 'missingAction' handler what renders the respective view. In case of failure (user is disallowed to use given action), ACFilter throws appropriate exception and access is effectively denied. Consequently, as ecpected "About' page is availabe to anybody while 'Help" page is only accessible by registered users. The access controll for static pages is now driven in the same manner as for dynamic ones, so we maintain convention consistency. I hope that could be helpful for the Yii community. Rgs,
  13. As I am talking about simple site, Iwouldlike to keep all access rules in Controller.php - common for entire website. So, there can be something like: array( 'allow', 'actions'=>array('about'), 'users'=>array('*'), ), array( 'allow', 'actions'=>array('help'), 'users'=>array('@'), ), array( 'deny', 'users'=>array('*'), ), as you can see, according these rules everybody can access (static) page 'About' but only registered users can access (also static) page 'Help'. Of course, in general sense you may have more specific rules for particular static pages (including IP rules). But in my implementation all pages are served through 'missingAction' handler and none of these rules is applied! Consequently, I would like to evaluate these rules in explicit manner - preferably 'parsing' all respective data ($user,$controller,$action,$ip,$verb) through CAccessRule 'isUserAllowed' public method to get authoritative feedback from the filter. But I do not know what is the right way to access CAccessControlFilter...
  14. Antionio, I really apreciate - good lesson in OOP coding style! BTW - if you just know - would yu be so kind to let me know how to call CAccessControlFilter in an explicit manner? Simply speaking, I wanted to implement some basic accessRule chcecking for static pages (not RBAC) so, to resolve user access right to given action I need to use its methods... Frankly speaking - this is always confusing... is there some 'magic skill' necessary to read documentation in a purpose to guess how to call given class within an application scope?
  15. Here you are typical simple usage scenario: public function accessRules(){ return array( array( // these pages are accessible by all (AJAX captcha is included here) 'allow', 'actions'=>array('index','about','help','contact','error','captcha',), 'users'=>array('*'), ), array( // these pages are for non-logged-in users only 'allow', 'actions'=>array('login',), 'users'=>array('?'), ), array( // these pages are for logged-in users only 'allow', 'actions'=>array('manage','logout',), 'users'=>array('@'), ), array( // block access to everything what was not explicite allowed // and send the user back to where he/she came from 'deny', 'deniedCallback' => array($this, 'redirectBack'), 'users'=>array('*'), ), ); } Please notice, the order of rules (from top to bottom) is important! And of course you need to add the following to your 'Controller.php' in components folder: public function filters(){ return array('accessControl'); // perform access control on all controllers extending this class } public function redirectToHome(){ // send to homesite $this->redirect(Yii::app()->baseUrl); } public function redirectBack(){ // send the rejected user to where he/she came from $zs_referer = Yii::app()->request->urlReferrer; if($zs_referer == ''){ $this->redirect(Yii::app()->baseUrl); }else{ $this->redirect($zs_referer); } }
  16. @Edward Between simple "add static page" and full-featured CMS a huge gap exists - please, do not overestimate my capabilities :-) But very often I have demand from customers to let them manage static pages by their own - sort of minimalistic approach they use for special communication, promotions, extra bit of information - something what was unexpected and unpredictable but does not require special sophistication. Presuming they have the application layout template - they can assemble simple static pages themselves - even in WordPad, drop them into 'static' folder and - wow! - they can immediately link these pages in their e-mails as they are served within the application scope readily! And on top of that - I strongly believe from aestetical point of view and code reusability - my solution seems to be simply more clean... But of course - if you know how to achive the same using some "magical' routing rules I am not aware of - as I confirm - I do not understand all these rules well enough - you can share. No offence! @HartleySan Sure - thank you for your very kind moderation! I really appreciate!
  17. OK, Here you are my "generic solution v. 1.0" : Assumption: All static pages are residing in a single folder: protected/views/static. No pages in subdirectories! We use UrlManager normally to serve clean URLs: 'urlManager'=>array( 'showScriptName'=>false, 'urlFormat'=>'path', 'rules'=>array( '<controller:\w+>/<id:\d+>'=>'<controller>/view', '<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', '<controller:\w+>/<action:\w+>'=>'<controller>/<action>', ), ), as well as .htaccess: <ifModule mod_rewrite.c> # Turn on the engine: RewriteEngine on # Do not perform redirects for files and directories that exist: RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # For everything else, redirect to index.php: RewriteRule . index.php </ifModule> Then we create a 'StaticControler.php': <?php class StaticController extends Controller { private $akcja; private function setAkcja($akcja) { $this->akcja = $akcja; } public function getAkcja() { return $this->akcja; } /** * This is the action to render static files as missing actions. */ public function missingAction($akcja){ $this->setAkcja($akcja); $views = $this->getViewPath(); if(file_exists($views . "/" . $akcja . ".php")){ $this->render($akcja); }else{ throw new CHttpException(404,'Page not found!'); } } } as well as a new component 'XMenu.php': <?php Yii::import('zii.widgets.CMenu'); class XMenu extends CMenu { protected function getStaticPage() { $ctrl = $this->getController(); if($ctrl->id == 'static'){ return $ctrl->getAkcja(); }else{ return '/'; } } protected function isItemActive($item, $route) { if($route == 'static') { $akcja = (explode('/', $item[url][0])); if($akcja[2] == $this->getStaticPage()) { return true; } } return parent::isItemActive($item,$route); } } Then we generate a menu like this: <div id="mainmenu"> <?php $this->widget('XMenu',array( 'items'=>array( array('label'=>'Home', 'url'=>array('/site/index')), array('label'=>'About', 'url'=>array('/static/about'), 'url'=>array('/static/about')), array('label'=>'License', 'url'=>array('/static/license'), 'url'=>array('/static/license')), array('label'=>'Contact', 'url'=>array('/site/contact')), array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest), array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!Yii::app()->user->isGuest) ), )); ?> </div><!-- mainmenu --> You may see all is working as expected - you put a view file into the 'static' folder, you add a position into a menu in a regular manner and bingo - new static page is served with a clean URL and menu items are highlited flawlessly. No more nasty routing rules, no more countless controller actions. Perhaps there are more clever solutions - you are welcome to share! Rgs, Ziggi
  18. Edward, Of course Sasha Makarov is right! But this is not the solution I am looking for !!! Please - try to understand that in my humble opinion adding: '<alias:page1>' => 'website/page1', '<alias:page2>' => 'website/page2', '<alias:page3>' => 'website/page3', to routing rules, as well as adding: public function actionPage1($alias) { echo "Page is $alias."; } public function actionPage2($alias) { echo "Page is $alias."; } public function actionPage3($alias) { echo "Page is $alias."; } in case of each and every new static page added to a website is a COMPLETE DISASTER !!! Simply speaking - this is the most nasty way I could ever imagine to handle static pages! Having static pages organized like that it is impossible to add new ad-hoc menuitems programatically. I am looking for a clean generic solution: - one routing rule - one controller - clean URLs for ALL static pages. - Can you cope now??? Best regards, Ziggi
  19. Larry - as a relatively new Yii user I find one of the most difficult part of the game finding how given class can be addressed. I mean - it is often difficult to guess how to use given class' method in the application scope as it is often unclear is given class already instancialized or not, should given method be called statically or rather through an object, etc. You may see very often people asking questions like "how to call a method of one controller from another one" etc. This is all about the issue I tried to describe above. I do not know is it possible to give some general advices how to read documentation to resolve such issues but perhaps you know better and can share...
  20. @Antonio Thank you for attempting to help me. Sorry again for too much disputing. But we are just nomal people - we should not keep anger in heart. I apologise you and Edward. Please forgive. I need some time to think about your feedback on static pages. For the time being I was thinking about setting a menu item 'active' through passing missingActionID to extended CMenu and comparing it with menu routing. If missingActionID == url then set menuitem active...
  21. @HartleySan I can agree my INITIAL post was dictated by frustration after spending hours trying to fit information collected from Yii wiki and Larry's book to my problem. And as soon as Larry answered - I tried to explain my problem in a very precise manner. This is why I found Edwards' post so confusing - it was like "hi man but.... did you check you have a power plugged on?" - this sort of feedback is acting like red scarf on a bully. Honestly speaking - now I seriously doubt Edward really ever read my post #3. Most probably he just passed his eyes over my initial post and provided very routine, obvious and... pointless answer. @All Well - I am not on the position to make enemies on discussion fora. This is not a good way. If I offenced someone or even hurt - I feel sory. The only excuse I may have is that I found Edward's feedback so... laying me off. I do not know is this enough for the group but I repeat - I always try to be friendly. If incidentally I know an answer - I provide a clear feedback without unnecessary disputes. I do not complain that I "open my cards against a person who did not even try to find the answer through reading books or spending hours on documentation". This is not my point to judge why the question was asked. If I would not want to be helpful I simply did not bother to read someone's posts... So please - do not think I am a nasty bug as I am not. I apologize once more - sure I could be more modest - that is right. I hope you can accept it. Rgs, Ziggi
  22. For the time being you were so kind to provide me one worthy feedback - when I asked about user folders... But not this time. Actually I start to believe you simply miss my point and try to guide me to answer not really matching my problem. So, I continue developing my "workaround" extending CMenu to highlite active menu items pointing to static pages. Perhaps this is not the best solution, perhaps I turned the wrong way but I will not beg you on my knees... Sorry if you really found my attitude wrong but this is more about personal choices. If I know the answer - I give the solution in plain language. I do not pretend a "professor" like you do.
  23. And last but not least - you may try Googling yourself: try please this phrase: yii clean url static pages You may find many results and no working solution actually. At least not working as it should: like this one: http://learnyiiframework.blogspot.com/2012/07/creating-url-rules-for-static-pages-in.html So - IMHO - this is not that obvious. Rgs, Ziggi
  24. >> you demand an explanation to an undefined problem... My entire post #3 in this thread is defining the problem very precisely, I even tried to explain my "workaround". If this is not enough than I do not know what can be more comprehensive... Sorry - I completely do not understand this sentence of yours: "This is a book covering 22 books, with YII as the newest addition." What do you mean? What 22 books you are talking about? I do not cope at all.
  25. Sorry, I do not even see where I was unpolite. Is "hi, Yii guys" unpolite? Can you tell me exactly what my phrase was so unpolite actually? And I do not understand what is the value of Edward's "advisory" not really more helpful than "Google it!". IMHO - what is this forum for? What is Larry's book for? Is it for people who already know Yii by heart? For renown Yii experts? Nope - this is for the people like me - who just started with this framework and have many issues. And if I tell you given subject is unclear - that means it is - it least for beginners. Think a little bit: if Larry is asking the readers of his book for "helpful opinions" - does he expect advices like "here you are a link to a website about proper English - go there and figure out on your own" - can you cope? This is not very friendly at all ! I repeat - If I were able to "figure it out" based on the link provided - I would not bother you asking. Rgs, Ziggi
×
×
  • Create New...