Posts Issued in July, 2014

First ever lines of code in Py. For testing go to cloud www.codesculptor.org and paste the code in canvas, then press run. Another implementation of Spaceship - program template for RiceRocks - last project in the course. Week 7 - 8.

# implementation of Spaceship
 
################################################################
#
# PLEASE CHANGE THE SOUND_FORMAT TO "mp3" IF SOUND DOESN'T WORK
#
################################################################
import simplegui
import math
import random
 
# globals for user interface
SOUND_FORMAT = "ogg"
WIDTH = 800
HEIGHT = 600
EXPLOSION_WIDTH = 128
score = 0
lives = 3
time = 0.5
rock_num = 0
started = False
 
class ImageInfo:
    def __init__(self, center, size, radius = 0, lifespan = None, animated = False):
        self.center = center
        self.size = size
        self.radius = radius
        if lifespan:
            self.lifespan = lifespan
        else:
            self.lifespan = float('inf')
        self.animated = animated
 
    def get_center(self):
        return self.center
 
    def get_size(self):
        return self.size
 
    def get_radius(self):
        return self.radius
 
    def get_lifespan(self):
        return self.lifespan
 
    def get_animated(self):
        return self.animated
 
 
# art assets created by Kim Lathrop, may be freely re-used in non-commercial projects, please credit Kim
 
# debris images - debris1_brown.png, debris2_brown.png, debris3_brown.png, debris4_brown.png
#                 debris1_blue.png, debris2_blue.png, debris3_blue.png, debris4_blue.png, debris_blend.png
debris_info = ImageInfo([320, 240], [640, 480])
debris_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png")
 
# nebula images - nebula_brown.png, nebula_blue.png
nebula_info = ImageInfo([400, 300], [800, 600])
nebula_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/nebula_blue.png")
 
# splash image
splash_info = ImageInfo([200, 150], [400, 300])
splash_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/splash.png")
 
# ship image
ship_info = ImageInfo([45, 45], [90, 90], 35)
ship_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/double_ship.png")
 
# missile image - shot1.png, shot2.png, shot3.png
missile_info = ImageInfo([5,5], [10, 10], 3, 50)
missile_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/shot2.png")
 
# asteroid images - asteroid_blue.png, asteroid_brown.png, asteroid_blend.png
asteroid_info = ImageInfo([45, 45], [90, 90], 40)
asteroid_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blue.png")
 
# animated explosion - explosion_orange.png, explosion_blue.png, explosion_blue2.png, explosion_alpha.png
explosion_info = ImageInfo([64, 64], [128, 128], 17, 24, True)
explosion_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/explosion_alpha.png")
 
# sound assets purchased from sounddogs.com, please do not redistribute
# .ogg versions of sounds are also available, just replace .mp3 by .ogg
soundtrack = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/soundtrack."+SOUND_FORMAT)
missile_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/missile."+SOUND_FORMAT)
missile_sound.set_volume(.5)
ship_thrust_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/thrust."+SOUND_FORMAT)
explosion_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/explosion."+SOUND_FORMAT)
 
# helper functions to handle transformations
def angle_to_vector(ang):
    return [math.cos(ang), math.sin(ang)]
 
def dist(p, q):
    return math.sqrt((p[0] - q[0]) ** 2 + (p[1] - q[1]) ** 2)
 
 
# Ship class
class Ship:
 
    def __init__(self, pos, vel, angle, image, info):
        self.pos = [pos[0], pos[1]]
        self.vel = [vel[0], vel[1]]
        self.thrust = False
        self.angle = angle
        self.angle_vel = 0
        self.image = image
        self.image_center = info.get_center()
        self.image_size = info.get_size()
        self.radius = info.get_radius()
 
    def draw(self,canvas):
        if self.thrust:
            canvas.draw_image(self.image, [self.image_center[0] + self.image_size[0], self.image_center[1]] , self.image_size,
                              self.pos, self.image_size, self.angle)
        else:
            canvas.draw_image(self.image, self.image_center, self.image_size,
                              self.pos, self.image_size, self.angle)
        # canvas.draw_circle(self.pos, self.radius, 1, "White", "White")
 
    def update(self):
        # update angle
        self.angle += self.angle_vel
 
        # update position
        self.pos[0] = (self.pos[0] + self.vel[0]) % WIDTH
        self.pos[1] = (self.pos[1] + self.vel[1]) % HEIGHT
 
        # update velocity
        if self.thrust:
            acc = angle_to_vector(self.angle)
            self.vel[0] += acc[0] * .1
            self.vel[1] += acc[1] * .1
 
        self.vel[0] *= .99
        self.vel[1] *= .99
 
    def set_thrust(self, on):
        self.thrust = on
        if on:
            ship_thrust_sound.rewind()
            ship_thrust_sound.play()
        else:
            ship_thrust_sound.pause()
 
    def increment_angle_vel(self):
        self.angle_vel += .05
 
    def decrement_angle_vel(self):
        self.angle_vel -= .05
 
    def shoot(self):
        global a_missile
        forward = angle_to_vector(self.angle)
        missile_pos = [self.pos[0] + self.radius * forward[0], self.pos[1] + self.radius * forward[1]]
        missile_vel = [self.vel[0] + 6 * forward[0], self.vel[1] + 6 * forward[1]]
        missile_group.add(Sprite(missile_pos, missile_vel, self.angle, 0, missile_image, missile_info, missile_sound))
        #a_missile = Sprite(missile_pos, missile_vel, self.angle, 0, missile_image, missile_info, missile_sound)
 
 
 
# Sprite class
class Sprite:
    def __init__(self, pos, vel, ang, ang_vel, image, info, sound = None):
        self.pos = [pos[0],pos[1]]
        self.vel = [vel[0],vel[1]]
        self.angle = ang
        self.angle_vel = ang_vel
        self.image = image
        self.image_center = info.get_center()
        self.image_size = info.get_size()
        self.radius = info.get_radius()
        self.lifespan = info.get_lifespan()
        self.animated = info.get_animated()
        self.age = 0
        if sound:
            sound.rewind()
            sound.play()
 
    def draw(self, canvas):
        draw_center = self.image_center
        if self.animated:
            draw_center = [self.image_center[0] + self.age * EXPLOSION_WIDTH, self.image_center[1]]
        canvas.draw_image(self.image, draw_center, self.image_size,
                          self.pos, self.image_size, self.angle)
 
    def update(self):
        # update angle
        self.angle += self.angle_vel
 
        # update position
        self.pos[0] = (self.pos[0] + self.vel[0]) % WIDTH
        self.pos[1] = (self.pos[1] + self.vel[1]) % HEIGHT
 
        self.age += 1
 
    def collide(self, other_object):
        if dist(self.pos, other_object.pos) < self.radius + other_object.radius:
            return True
        else:
            return False
 
def group_collide(group, other_object):
    group_copy = set(group)
    collide_num = 0
    for an_object in group_copy:
        if an_object.collide(other_object):
            group.remove(an_object)
            collide_num += 1
    return collide_num
 
def group_group_collide(group1, group2):
    group_copy = set(group1)
    collide_num = 0
    for an_obj1 in group_copy:
        if group_collide(group2, an_obj1):
            explosion_group.add(Sprite(an_obj1.pos, [0, 0], 0, 0, explosion_image, explosion_info))
            group1.remove(an_obj1)
            collide_num += 1
    return collide_num
 
def process_sprite_group(sprite_group, canvas):
    for a_sprite in sprite_group:
        a_sprite.draw(canvas)
        a_sprite.update()
        if a_sprite.age > a_sprite.lifespan:
            sprite_group.remove(a_sprite)
 
# key handlers to control ship   
def keydown(key):
    #if started == False:
        #return None
    if key == simplegui.KEY_MAP['left']:
        my_ship.decrement_angle_vel()
    elif key == simplegui.KEY_MAP['right']:
        my_ship.increment_angle_vel()
    elif key == simplegui.KEY_MAP['up']:
        my_ship.set_thrust(True)
    elif key == simplegui.KEY_MAP['space']:
        my_ship.shoot()
 
def keyup(key):
    if key == simplegui.KEY_MAP['left']:
        my_ship.increment_angle_vel()
    elif key == simplegui.KEY_MAP['right']:
        my_ship.decrement_angle_vel()
    elif key == simplegui.KEY_MAP['up']:
        my_ship.set_thrust(False)
 
# mouseclick handlers that reset UI and conditions whether splash image is drawn
def click(pos):
    global started, lives, score
    center = [WIDTH / 2, HEIGHT / 2]
    size = splash_info.get_size()
    inwidth = (center[0] - size[0] / 2) < pos[0] < (center[0] + size[0] / 2)
    inheight = (center[1] - size[1] / 2) < pos[1] < (center[1] + size[1] / 2)
    lives = 3
    score = 0
    if (not started) and inwidth and inheight:
        started = True
 
def draw(canvas):
    global time, started, lives, score
 
    # animiate background
    time += 1
    center = debris_info.get_center()
    size = debris_info.get_size()
    wtime = (time / 8) % center[0]
    canvas.draw_image(nebula_image, nebula_info.get_center(), nebula_info.get_size(), [WIDTH / 2, HEIGHT / 2], [WIDTH, HEIGHT])
    canvas.draw_image(debris_image, [center[0] - wtime, center[1]], [size[0] - 2 * wtime, size[1]], 
                                [WIDTH / 2 + 1.25 * wtime, HEIGHT / 2], [WIDTH - 2.5 * wtime, HEIGHT])
    canvas.draw_image(debris_image, [size[0] - wtime, center[1]], [2 * wtime, size[1]], 
                                [1.25 * wtime, HEIGHT / 2], [2.5 * wtime, HEIGHT])
 
    # draw ship and sprites
    my_ship.draw(canvas)
    my_ship.update()
    process_sprite_group(rock_group, canvas)
    process_sprite_group(missile_group, canvas)
    process_sprite_group(explosion_group, canvas)
    if group_collide(rock_group, my_ship):
        lives -= 1
        explosion_group.add(Sprite(my_ship.pos, [0, 0], 0, 0, explosion_image, explosion_info))
        if lives <= 0:
            started = False
            rock_group.intersection_update(set())
            #lives = 3
            #score = 0
    # draw UI
    canvas.draw_text("Lives", [50, 50], 22, "White")
    canvas.draw_text("Score", [680, 50], 22, "White")
    canvas.draw_text(str(lives), [50, 80], 22, "White")
    canvas.draw_text(str(score), [680, 80], 22, "White")
 
    missile_hit_number = group_group_collide(rock_group, missile_group)
    score += missile_hit_number * 10
    # draw splash screen if not started
    if not started:
        canvas.draw_image(splash_image, splash_info.get_center(), 
                          splash_info.get_size(), [WIDTH / 2, HEIGHT / 2], 
                          splash_info.get_size())
 
# timer handler that spawns a rock    
def rock_spawner():
    #global rock_num
    if len(rock_group) >= 12 or started == False:
        return None
    rock_pos = [random.randrange(0, WIDTH), random.randrange(0, HEIGHT)]
    rock_vel = [random.random() * .6 - .3, random.random() * .6 - .3]
    rock_avel = random.random() * .2 - .1
    rock_group.add(Sprite(rock_pos, rock_vel, 0, rock_avel, asteroid_image, asteroid_info))
    soundtrack.rewind()
    #soundtrack.play()
    #rock_num += 1
    #a_rock = Sprite(rock_pos, rock_vel, 0, rock_avel, asteroid_image, asteroid_info)
 
# initialize stuff
frame = simplegui.create_frame("Asteroids", WIDTH, HEIGHT)
 
# initialize ship and two sprites
my_ship = Ship([WIDTH / 2, HEIGHT / 2], [0, 0], 0, ship_image, ship_info)
rock_group = set()
#a_rock = Sprite([WIDTH / 3, HEIGHT / 3], [1, 1], 0, .1, asteroid_image, asteroid_info)
missile_group = set()
explosion_group = set()
#a_missile = Sprite([2 * WIDTH / 3, 2 * HEIGHT / 3], [-1,1], 0, 0, missile_image, missile_info, missile_sound)
 
 
# register handlers
frame.set_keyup_handler(keyup)
frame.set_keydown_handler(keydown)
frame.set_mouseclick_handler(click)
frame.set_draw_handler(draw)
 
timer = simplegui.create_timer(1000.0, rock_spawner)
 
soundtrack.play()
# get things rolling
timer.start()
frame.start()

This widget will show a Facebook "like" button and "comments" box together with "tweet" button and "google plusone" button on your page.

class SimpleShare extends CWidget
{
    /**
     * Site Name
     * @var string
     * defaults to Yii::app()->name
     */
    public $siteName = '';
 
    /**
     * Site Administrator's Facebook ID
     * @var string
     */
    public $fbSiteAdmin = 'XXXXXXXXXXXXXX';
 
    /**
     * URL of the page
     * @var string
     * defaults to the current page URL
     */
    public $pageUrl = '';
 
    /**
     * Title of the page
     * @var string
     */
    public $pageTitle = '';
 
    /**
     * Type of the Page : eg. website, article, ... etc.
     * @var string
     * defaults to 'article'
     */
    public $pageType = '';
 
    /**
     * Description of the page
     * @var string
     */
    public $pageDescription = '';
 
    /**
     * Image(s) of the page
     * @var mixed
     * can be a single string or array of strings
     * defaults $this->defaultPageImage
     */
    public $pageImages = '';
 
    /**
     * Default image of the page
     * @var string
     */
    public $defaultPageImage = '/images/fb/site-logo.jpg';
 
    /**
     * Show Comments
     * @var boolean
     * defaults to true
     */
    public $showComments = true;
 
    /**
     * Minimum IE version required
     * @var string
     */
    public $minimumIEVersion = '8';
 
    /**
     * Initialization
     * @see CWidget::init()
     */
    public function init()
    {
        parent::init();
 
        // Site Name
        if ($this->siteName == '') {
            $this->siteName = Yii::app()->name;
        }
 
        // base URL
        $baseUrl = Yii::app()->request->hostInfo . Yii::app()->request->baseUrl;
        // URL of the page
        if ($this->pageUrl == '') {
            $this->pageUrl = $baseUrl . '/' . Yii::app()->request->pathInfo;
        }
 
        // Type of the page
        if ($this->pageType == '') {
            $this->pageType = 'article';
        }
 
        // Set opengraph meta tags
        /** @var CClientScript $cs */
        $cs = Yii::app()->getClientScript();
        $cs->registerMetaTag($this->siteName, NULL, NULL, array('property'=>'og:site_name'));
        $cs->registerMetaTag($this->fbSiteAdmin, NULL, NULL, array('property'=>'fb:admins'));
        $cs->registerMetaTag($this->pageUrl, NULL, NULL, array('property' =>'og:url'));
        $cs->registerMetaTag($this->pageTitle, NULL, NULL, array('property'=>'og:title'));
        $cs->registerMetaTag($this->pageType, NULL, NULL, array('property'=>'og:type'));
        // Description of the page
        if ($this->pageDescription != "") {
            $cs->registerMetaTag($this->pageDescription, NULL, NULL, array('property'=>'og:description'));
        }
        // Image(s) of the page
        if (is_array($this->pageImages)) {
            if (count($this->pageImages) == 0) {
                $this->pageImages = $this->defaultPageImage;
            }
            foreach($this->pageImages as $image) {
                $cs->registerMetaTag($baseUrl . $image, NULL, NULL, array('property'=> 'og:image'));
            }
        } else {
            if ($this->pageImages == "") {
                $this->pageImages = $this->defaultPageImage;
            }
            $cs->registerMetaTag($baseUrl . $this->pageImages, NULL, NULL, array('property'=> 'og:image'));
        }
 
        // javasctipt to enable the gadgets
        $init_js = < < < I N I T _ J S
var msie = navigator.appVersion.toLowerCase();
msie = (msie.indexOf('msie')>-1) ? parseInt(msie.replace(/.*msie[ ]/,'').match(/^[0-9]+/)) : 0;
if (msie == 0 || msie >= $this->minimumIEVersion) {
    $('#sns-share').show();
    // google plus one
    window.___gcfg = {
        lang: 'ja'
    };
    (function() {
        var po = document.createElement('script');
        po.type = 'text/javascript';
        po.async = true;
        po.src = 'https://apis.google.com/js/plusone.js';
        var s = document.getElementsByTagName('script')[0];
        s.parentNode.insertBefore(po, s);
    })();
    // twitter
    !function(d,s,id){
        var js,fjs=d.getElementsByTagName(s)[0],
        p=/^http:/.test(d.location)?'http':'https';
        if(!d.getElementById(id)){
            js=d.createElement(s);
            js.id=id;
            js.async=true;
            js.src=p+'://platform.twitter.com/widgets.js';
            fjs.parentNode.insertBefore(js,fjs);
        }
    }(document, 'script', 'twitter-wjs');
    // facebook
    (function(d, s, id) {
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) return;
        js = d.createElement(s); js.id = id;
        js.async=true;
        js.src = "//connect.facebook.net/ja_JP/all.js#xfbml=1";
        fjs.parentNode.insertBefore(js, fjs);
    }(document, 'script', 'facebook-jssdk'));
}
INIT_JS;
        $cs->registerScript('init-sns-share', $init_js, CClientScript::POS_READY);
    }
 
    /**
     * Display the widget
     * @see CWidget::run()
     */
    public function run()
    {
        echo '<div id="sns-share" style="di splay:none">' . "\n";
        echo '<h3>Share the page with Facebook, twitter and google plusone</h3>' . "\n";
        echo '<div class="sns-share-buttons">' . "\n";
 
        // google plusone
        echo '<div class="google-plus" style="float:right">' . "\n";
        echo '<g:plusone size="medium" href="' . $this->pageUrl . '"></g:plusone>' . "\n";
        echo '</div>' . "\n";
 
        // twitter
        $tw_text = $this->siteName . ' - ' . $this->pageTitle;
        if ( $this->pageDescription != '')
        {
            $tw_text .= ' : ' . $this->pageDescription;
        }
        echo '<div class="tweet-button" style="float:right">' . "\n";
        echo '<a href="https://twitter.com/share" '
                . 'class="twitter-share-button" '
                . 'data-url="' . $this->pageUrl . '" '
                . 'data-text="' . $tw_text . '" '
                . 'data-count="horizontal">Tweet</a>' . "\n";
        echo '</div>' . "\n";
 
        // facebook
        echo '<div class="fb-like" '
                . 'data-href="' . $this->pageUrl . '" '
                . 'data-send="true" '
                . 'data-width="500" '
                . 'data-show-faces="false"></div>' . "\n";
        if ($this->showComments)
        {
            echo '</div>' . "\n";
            echo '<div class="facebook-comments">' . "\n";
            echo '<div class="fb-comments" '
                    . 'data-href="' . $this->pageUrl . '" '
                    . 'data-num-posts="4" '
                    . 'data-width="600"></div>' . "\n";
        }
        echo '</div>' . "\n";
        echo '</div>' . "\n";
    }
}
 
//Usage: put the widget in view like the following.
 
<?php
$this->widget('SimpleShare', array(
    'pageTitle' => 'The title of the page.',
    'pageDescription' => 'The long descriptions of the page.',
    'pageType' => 'article',
    'pageImages' => array('/images/001.jpg', '/images/002.jpg'),
));
?>
 
/*'pageImages' can be an array of strings (up to 5 images).
You may want to use the default values for 'pageType' and 'pageImages'.
*/
<?php
$this->widget('SimpleShare', array(
    'pageTitle' => 'The title of the page.',
    'pageDescription' => 'The long descriptions of the page.',
));
?>
  • Gen app
  • Configuring
  • Editing models
  • Basic View Edits
  • Basic Controller Edits

Gen app cd /Users/larryullman/Sites/YiiBlogSite/framework yiic webapp path/to/directory

Configuring

//index.php
$yii=dirname(__FILE__).'/../framework/yii.php';
 
//The second line identifies where the configuration file is:
$config=dirname(__FILE__).'/protected/config/main.php';
/*The default behavior is to put the protected directory, where all the application files reside, in the same directory as the index file. My inclination is to move it outside of the Web directory. Moving the protected folder outside of the Web root directory is just an extra security precaution. It’s not required, and you may not want to bother with the change, especially as you’re just getting started. In such a case, I edit my index.php file to read:*/
$config= '../protected/config/main.php';
 
//The next line of code turns on debugging mode:
defined('YII_DEBUG') or define('YII_DEBUG',true);
 
/*You’ll want debugging enabled when developing a site, but disabled once live. To disable debuggin, remove or comment out that line.*/
//The next line of code dictates how many levels of “call stack” are shown in a message log:
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);

Most of the configuration occurs in the main.php configuration file, found within the protected/config directory

* 'name'=>'Wicked Cool Yii Site',
* 'gii'=>array(
* 'urlManager'=>array(
* 'db'=>array(
* 'log'=>array(
* 'params'=>array(
* “sitecontroller default. To change that add:
'defaultController' => 'login',

**Editing models*

public function rules()
{
    return array(
        array('departmentId, firstName, lastName, email, hireDate', 'required'),
        array('departmentId, ext', 'numerical', 'integerOnly'=>true),
        array('firstName', 'length', 'max'=>20),
        array('lastName', 'length', 'max'=>40),
        array('email', 'length', 'max'=>60),
        array('email', 'email'),
        array('leaveDate', 'safe'),
        array('id, departmentId, firstName, lastName, email, ext, hireDate, leaveDate', 'safe', 'on'=>'search'),
 );
}
public function relations()
{
    return array('department' => array(self::BELONGS_TO, 'Department', 'departmentId') );
}
public function relations()
{
    return array('employees' => array(self::HAS_MANY, 'Employee', 'departmentId') );
}
 
//attributeLabels() returns an associative array of fields and the labels to use for those fields in forms, error messages, and so forth. 
public function attributeLabels()
{
    return array(
        'id' => 'Employee ID',
        'departmentId' => 'Department',
        'firstName' => 'First Name',
        'lastName' => 'Last Name',
        'email' => 'Email',
        'ext' => 'Ext',
        'hireDate' => 'Hire Date',
        'leaveDate' => 'Leave Date',
    );
}

**Basic View Edits* - protected/views/layouts/main.php

//To start in the HEAD, you’ll see that external files are linked using
<link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/main.css" />
 
//Next, you’ll see the page’s title set dynamically:
<title><?php echo CHtml::encode($this->pageTitle); ?></title>
//The CHtml::encode() method is just used to protect against Cross-Site Scripting (XSS) attacks.
 
// in the main layout file:
<div id="logo"><?php echo CHtml::encode(Yii::app()->name); ?></div>
 
/*The create and update Views have some page header stuff, then include the form View, using this code:*/
<?php echo $this->renderPartial('_form', array('model'=>$model)); ?>
 
//Finally, you may decide you want to change the page’s title. To do that, use code like:
<?php $this->pageTitle = $model->something; ?>

Basic Controller Edits

//within a Controller class is a variable called $layout:
public $layout='//layouts/column2';
 
//default is actionIndex().
public $defaultAction='admin';
  • Textbox → Dropdownlist
  • Dropdownlist → Dropdownlist
  • Textbox → Textbox
  • JS code and action
  • Table row as a FORM
  • List Item
  • Textbox → Dropdownlist
View:
 
echo CHtml::form();
     // CHtml::textField($name, $value, $htmlOptions)
echo CHtml::textField('myTextField','',
array(
'ajax' =>
array(
'type'=>'POST', //request type
'url'=>CController::createUrl('myActionName'), //action to call
'update'=>'#updatedDropDownList', // which HTML element to update
)
));
 
     // CHtml::dropDownList($name, $select, $data, $htmlOptions)
echo CHtml::dropDownList('updatedDropDownList','',array(), array());
 
echo CHtml::endForm();
 
Whats also important is the word update. It says that referred dropDownList will be extended. In our case we will add some option values (rows). And here is the code of the underlying action:
 
public function actionMyActionName()
{
// CHtml::tag($tagName, $htmlOptions, $content, $closeTag)
echo CHtml::tag('option',
array('value'=>'1'),         // html params of tag
CHtml::encode('hello'), // caption, string may be enough. CHtml::encode() may not be necessary.
true                                   // close tag
); 
}
  • Dropdownlist → Dropdownlist
echo CHtml::form();**
     // CHtml::dropDownList($name, $selected, $values, $htmlOptions)
echo CHtml::dropDownList('country','',array(1=>'has value I',2=>'has value II'),
array(
'ajax' =>
array(
'type'=>'POST', //request type
'url'=>CController::createUrl('myActionName'), //action to call
'update'=>'#updatedDropDownList', // which HTML element to update
)
));
 
     // CHtml::dropDownList($name, $select, $data, $htmlOptions)
echo CHtml::dropDownList('updatedDropDownList','',array(), array());
 
**echo CHtml::endForm();**
 
Tha main difference here is that drop down boxes have to be in a FORM !! Otherwise their vales won't be accessible via POST in action !!
 
Below is appropriate action.
 
public function actionMyActionName()
{
$countryID = $_POST[‘country’]; // IMPORTANT .. this is how you access previously entered data
$listOfCities = getCitiesOfState($countryID);
foreach ($listOfCities as $city)
{
echo CHtml::tag('option',         // tagname
array('value'=>$cityID),         // html params of tag
$cityName, // value from the item selected in the first dropdown is in the POST array
true                                           // close tag
);
} 
}
  • Textbox → Textbox
echo CHtml::form();
echo CHtml::textField('myTextField','',
array(
'ajax' =>
array(
'type'=>'POST', //request type
'url'=>CController::createUrl('myActionName'), //action to call
'replace'=>'#statusTextBox’, // which HTML element to update
)
));
 
echo CHtml::textField(‘statusTextBox’,'',array());
 
echo CHtml::endForm();
 
In this case is important the **replace ** option. It sais that the original textField named statusTextBox will be replaced with a new one with different properties (text).
 
public function actionMyActionName()
{
$status = '';
if (strlen($_POST[‘myTextField’])<5)
{
$status = 'too small';
}
else
{
$status = 'size is OK';
}
echo CHtml::textField('statusTextBox', $status,array());
}
  • JS code and action
JS code would look like this:
 
$.get('editUser', { userId: 1 },  function(html){  // html variable contains code of updated <tr>
      $.fancybox.close();                       // closes Fancybox
      $.(this).closest('tr').replaceWith(html); // replaces current <tr> .. </tr> with new HTML code                    
    });
 
And action:
 
public function actionGetHelloWorldByAjax($userId)
  {
    $myHtml = $this->renderPartial('userRow',array('id'=>$userId),true); 
    echo $myHtml;
    Yii::app()->end(); // this ends Yii application in case of Ajax requests.                                  
    return;
  }
 
You can also generate the JS code in action and just evaluate it in browser. You will need JSON object:
 
$.getJSON('editUser', { userId: 1 },  function(result){  // result variable contains JSON object
      eval(result.js) // runs JS code, see action code below                                                                                
    });
 
And action:
 
public function actionGetHelloWorldByAjax($userId)
  {
    echo JSON::encode(array(
              'newRow'=> $this->renderPartial('userRow',array('id'=>$userId),true),
              'js'=>'$.fancybox.close(); $.(this).closest('tr').replaceWith(result.newRow);',  // this is the "js" used in javascript above.
      );
      // array(variable=>$value) will be encoded into JSON object and than it can be used in jQUery as "set of variables".
    Yii::app()->end();                                  
    return;
  }
  • Table row as a FORM
<table>
  <tr>
    <td>
      <input type="text" name="quantity" value="a">
    </td>
    <td>
      <select name="colour">
        <option value="red">RED</option>
        <option value="blue">BLUE</option>
      </select>
      <input type="radio" name="material" value="wood" checked="checked">wood
      <input type="radio" name="material" value="plastic">plastic
      <input type="radio" name="material" value="glass">glass
    </td>
    <td>
      <a href="#" class="rowSubmit">Save</a>
    </td>
  </tr>
</table>
 
But where to place the FORM that would make the function? Form can't be used, because of HTML validity. It has to be faked in jQuery cca like this:
 
<script type="text/javascript">
$(document).ready(function(){
  $(".rowSubmit").click(function()
  {
     var serialized = $(this).closest('tr').wrap('<form>').parent().serialize();
 
     $.get('url2action', serialized, function(data){
       // ... can be empty
       // $.fancybox({content:data});
     }); 
   });
});        
</script>

Создание виджета WordPress похоже на построение плагина, но является более простым и явным процессом. В простейшем случае понадобится один файл, в котором будет находиться весь код PHP. Для организации виджета требуется три основных функции:

function widget()
function update()
function form()


Скелет, на котором строится код, выглядит обычно следующим образом:

add_action( 'widgets_init', 'register_my_widget' ); // Загрузка виджета
 
function register_my_widget() {}            // Функция регистрации виджета
 
class My_Widget extends WP_Widget () {}         //Класс виджета (как пример)
 
function My_Widget() {}                 // Установки виджета
 
function widget() {}                    // Вывод виджета
 
function update() {}                    // Обновление виджета
 
function form() {}                  // Форма для опций виджета
  • Для начала нужно загрузить виджет с помощью функции “widgets_init“. add_action( 'widgets_init', 'register_my_widget' ); Для инициализации виджета используется функция, в которой наш виджет регистрируется в системе, чтобы к нему открылся доступ в разделе виджетов.
function register_my_widget() {
    register_widget( 'My_Widget' );
}
  • Весь код виджета заключен в класс. Имя класса имеет важное значение. Нужно помнить, что имя класса и имя функции регистрации должны совпадать. class My_Widget extends WP_Widget {} Теперь передадим некоторые установочные параметры в данный класс. Например, мы можем передать ширину и высоту. Также можно определить небольшое описание, которое может быть полезно при привязке виджета к коммерческой теме.
function My_Widget() {
    function My_Widget() {
        $widget_ops = array( 'classname' => 'example', 'description' => __('Виджет, который выводит имя автора ', 'example') );
        $control_ops = array( 'width' => 300, 'height' => 350, 'id_base' => 'example-widget' );
        $this->WP_Widget( 'example-widget', __('Example Widget', 'example'), $widget_ops, $control_ops );
    }
  • Функция widget()относится к выводу нашего виджета. Мы будем передавать в нее пару аргументов. Первый аргумент будет получен из темы, в нем передается название и другие параметры. А второй аргумент - экземпляр нашего класса.
function widget( $args, $instance )
//Затем мы извлекаем значения из аргумента, потому что они должны быть доступны локально.
extract( $args );

Затем мы устанавливаем название и другие значения для нашего виджета, которые можно поменять в меню виджета. Также используются специальные переменные $before_widget и $after_widget, значения которых устанавливается темой.

$title = apply_filters('widget_title', $instance['title'] );
$name = $instance['name'];
$show_info = isset( $instance['show_info'] ) ? $instance['show_info'] : false;
 
echo $before_widget;
 
// Выводим название виджета
if ( $title )
    echo $before_title . $title . $after_title;
 
// Выводим имя
if ( $name )
    printf( '<p>' . __('Привет! Меня зовут %1$s.', 'example') . '</p>', $name );
 
if ( $show_info )
    printf( $name );
 
echo $after_widget;
//Теперь функция update(). Данная функция получает установки пользователя и сохраняет их.
function update( $new_instance, $old_instance ) {
    $instance = $old_instance;
 
    //Strip tags from title and name to remove HTML
    $instance['title'] = strip_tags( $new_instance['title'] );
    $instance['name'] = strip_tags( $new_instance['name'] );
    $instance['show_info'] = $new_instance['show_info'];
 
    return $instance;
}
  • Теперь создадим шаблон формы, которая будет служить для ввода значений. Здесь пользователь будет определять установки и значения. Функция form() будет содержать код для создания полей ввода, чекбоксов и так далее. Прежде, чем приступить к созданию полей ввода информации, нужно определить значения по умолчанию.
//Устанавливаем параметры по умолчанию.
$defaults = array( 'title' => __('Пример', 'example'), 'name' => __('Иван Лбов', 'example'), 'show_info' => true );
$instance = wp_parse_args( (array) $instance, $defaults ); ?>
Теперь создаем поля ввода текста.
// Название виджета
<p>
    <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e('Название:', 'example'); ?></label>
    <input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" value="<?php echo $instance['title']; ?>" style="width:100%;" />
</p>
 
//Поле ввода текста
<p>
    <label for="<?php echo $this->get_field_id( 'name' ); ?>"><?php _e('Ваше имя:', 'example'); ?></label>
    <input id="<?php echo $this->get_field_id( 'name' ); ?>" name="<?php echo $this->get_field_name( 'name' ); ?>" value="<?php echo $instance['name']; ?>" style="width:100%;" />
</p>
 
// Чекбокс
<p>
    <input class="checkbox" type="checkbox" <?php checked( $instance['show_info'], true ); ?> id="<?php echo $this->get_field_id( 'show_info' ); ?>" name="<?php echo $this->get_field_name( 'show_info' ); ?>" />
    <label for="<?php echo $this->get_field_id( 'show_info' ); ?>"><?php _e('Сделать информацию публичной?', 'example'); ?></label>
</p>

Вот и весь код простого виджета, который выводит имя автора блога. Сохраните код в PHP файле, который надо разместить в папке темы. Затем надо вызвать его в файле functions.php, после чего виджет будет доступен для использования через панель администратора (раздел для виджетов). Источник урока:

CSS for tables

<div class="table-responsive">
<table class="table table-striped"></table>
<table class="table table-bordered"></table>
<table class="table table-hover"></table>
<table class="table table-condensed">
        <tr class="success">Color</tr>
        <tr class="active">Color</tr>
        <tr class="info">Color</tr>
</div

Example:

<div class="table-responsive"> 
 
    <table class="table table-bordered table-hover table-condensed table-striped hide">
            <thead>  
              <tr>  
                <th>Код </th>  
                <th>Название</th>  
                <th>Автор</th>  
                <th>Категория</th> 
                <th>Год</th>
                <th>Цена</th> 
              </tr>  
        </thead>
        <tbody>
 
        </tbody>
    </table>
    </div>

Виджет CAutoComplete. Принцип создания поля довольно прост. На странице нужно поместить обычное текстовое поле и назначить событию onKeyUp обработчик, который будет отправлять AJAX запросы серверу. В этих запросах нужно передавать введённый посетителем текст. Сервер ищет совпадения с этим текстом в БД и возвращает результат браузеру. JavaScript обработчик создаёт список с вариантами, полученными от сервера, и показывает его под полем.

Для работы виджет использует плагин Autocomplete библиотеки jQuery, виджет создаст текстовое поле и подключит все необходимые JS и CSS файлы, нужно только указать некоторые настройки. Код вставки виджета

$this->widget('CAutoComplete',
    array(
        'model'=>'countries',
        'name'=>'country',
        'url'=>array('countries/autocomplete'),
        'minChars'=>2,
    )
);

Во втором параметре необходимо передать массив с параметрами. Обязательными являются первые два. model – имя модели. name – атрибут name текстового поля. url – адрес скрипта AJAX запросы. minChars – минимальное количество символов, при котором выполняется отправка запроса. По мере ввода текста серверному скрипту будут отправляться AJAX запросы вида: index.php?r=countries/autocomplete&q=%D0%B0%D0%BB&limit=10&timestamp=1273944702297В параметре q передаётся введённый посетителем текст, а в параметре limit – максимальное количество подстановок. Обработка запроса:

public function actionAutoComplete() {
    if (isset($_GET['q'])) {
        $criteria = new CDbCriteria;
        $criteria->condition = 'c_name LIKE :country';
        $criteria->params = array(':country'=>$_GET['q'].'%');
         if (isset($_GET['limit']) && is_numeric($_GET['limit'])) {
            $criteria->limit = $_GET['limit'];
        }
        $countries = countries::model()->findAll($criteria);
        $resStr = '';
        foreach ($countries as $country) {
            $resStr .= $country->c_name."\n";
        }
        echo $resStr;
    }
}

Pешение «в лоб»

$ids = array(1, 2, 3);
$dataProvider=new CActiveDataProvider('User',array(
    'criteria'=>array(
        'condition'=>'id IN ('.implode(',', $ids).')',
    )
));
 
$this->render('admin',array(
    'model'=>$dataProvider,
));

Говнокод выглядит не очень красиво; если массив $ids окажется пустым, возникнет ошибка; полученные значения нужно проверить. Далее решение с помощью библиотеки Yii.

$model = User::model()->findAllByAttributes(array('id'=>array(1, 2, 3)));
$dataProvider=new CActiveDataProvider('User');
$dataProvider->setData($model);
 
$this->render('index',array(
    'dataProvider'=>$dataProvider,
));
Метод findAllByAttributes класса CActiveRecord позволяет искать записи в БД по названию поля и значению. При этом если значение является массивом, то используется оператор IN. Этот же самый код можно записать немного иначе.
$dataProvider=new CActiveDataProvider('User');
$dataProvider->criteria->addInCondition('id', array(1,2,3));
 
$this->render('index',array(
    'dataProvider'=>$dataProvider,
));
 
**Тестируем**
Попробуем в массиве передать строку.
$dataProvider->criteria->addInCondition('id', array(1,2,'test'));
В результате будет выполнен следующий запрос.   
SELECT * FROM 'tbl_user' WHERE "id" IN (1, 2, 0)
Т.е. текстовое значение было заменено нулем, что вполне логично, т.к. поле id имеет тип INT.
Если запрос будет выполняться к полю текстового типа, то текстовые и числовые значения будут вставлены в кавычках.
 
$dataProvider->criteria->addInCondition('id', array(1, 'admin','user'));
сформирует такой запрос
SELECT * FROM 'tbl_user' WHERE "id" IN ('1','admin', 'user')
А если массив окажется пуст, то фреймворк гарантирует, что из таблицы не будет выбрана ни одна запись.
$dataProvider->criteria->addInCondition('id', array());
SELECT * FROM 'tbl_user' WHERE 0=1

B Yii включили в репозиторий packagist.org, теперь можно использовать один менеджер зависимостей Composer для обновления и фреймворка, и дополнительных библиотек.

  • Устанавливаем Composer. Для Windows есть инсталлятор. B Linux или MacOS нужно выполнить несколько команд из консоли.
  • Создаём папку для приложения, например yii-composer. Т.е. приложение доступно по адресу http://localhost/yii-composer
  • Создаем composer.json - хранятся названия компонентов, которые будет загружать Composer { "require": { "yiisoft/yii": "dev-master", "imagine/Imagine": "dev-master" } }
  • Загружаем компоненты. Для этого нужно выполнить команду: composer install или composer update
  • В результате Composer загрузит указанные пакеты, и создаст структуру папок. Composer создал папку vendor и загрузил в неё библиотеки imagine и фреймворк
  • Создаем приложение - стандартную утилиту yiic. Если приложение должно находиться в папке public_html, то из папки /vendor/yiisoft/yii/framework выполняем команду yiic webapp ../../../../public_html Yiic создаст нужные файлы, и мы увидим результат по адресу. http://localhost/yii-composer/public_html
  • Подключаем библиотеки, загруженные с помощью Composer. Composer автоматически создаёт загрузчик (файл vendor/autoload.php), который соответствует спецификации PSR-0. На практике это означает, что для того, чтобы использовать библиотеки, загруженные с помощью Composer, достаточно подключить загрузчик в файле index.php фреймворка, то есть
require_once('../vendor/autoload.php');
Теперь можно использовать Imagine. 
$imagine = new Imagine\Gd\Imagine();

При создании объекта необходимо путь к классу Imagine начиная от папки vendor/imagine/lib. Т.е. фактически без разницы, где именно Composer хранит библиотеки. Можно использовать примеры из документации к Imagine без какой-либо дополнительной настройки. Достигается это за счёт того, что Composer автоматически создаёт файл vendor/composer/autoload_namespaces.php, который возвращает реальное размещение библиотек. В данном случае:

return array(
    'Imagine' => $vendorDir . '/imagine/Imagine/lib/',
);

Если надо импортирвоать в БД средстввами рнр

public static function exportCSV($file)
    {
        $f = f o p e n($file, 'r');
        // самая первая строка - заголовки столбцов - мне не нужна
        $data = fgetcsv($f, 1000, ';');
        $db = mysql_connect($host, $user, $pass)   or die("Невозможно подключиться к серверу");
 
        mysql_select_db($bd_name, $db) or die("Ошибка выбора БД");
         while(!feof($f)) {
          $data = fgetcsv($f, 1000, ';');
          // Уникальный идентификатор записи
          $data[0] = (int) $data[0];
           $data[0] = addslashes(trim($data[0]));
           $data[1] = addslashes(trim($data[1]));
           $data[2] = addslashes(trim($data[2]));
           $data[3] = addslashes(trim($data[3]));
           $data[4] = addslashes(trim($data[4]));
           $data[5] = addslashes(trim($data[5]));
           $data[6] = addslashes(trim($data[6]));
 
          $query = "INSERT INTO table(id,category,phone, email,title,address,description)
            VALUES('".$data[0]."','".$data[1]."','".$data[2]."','"
                    .$data[3]."','".$data[4]."','".$data[5]."','".$data[6]."')";
 
          mysql_query($query, $db) or die ("Ошибка записи в БД");
        }
        mysql_close();
    }

Go to page: