起因

由于最近在做的一个网站中想把管理员跟普通用户分离开来,所以需要给所有的 用户添加一个admin,用来判断此用户是不是管理员。本来以为直接使用 Yii::app()->user->admin=1 就可以完成这个功能,结果发现会出错,看来不 能像python一样直接动态添加属性。所以就研究了一下Yii的CWebUser类和 CUserIdentity,得到如下解决方法。

自定义WebUser

在components目录中添加WebUser.php文件,内容如下,实现了admin参数的设定 和读取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
// 自定义用户类

class WebUser extends CWebUser {
// 获取admin值
public function getAdmin() {
if (($admin = $this->getState('__admin')) !== null)
return $admin;
else
return 0;
}
// 设置admin值
public function setAdmin($value) {
$this->setState('__admin', $value);
}
// 改变用户身份
protected function changeIdentity($id, $name, $states, $admin) {
Yii::app()->getSession()->regenerateID(true);
$this->setId($id);
$this->setName($name);
$this->setAdmin($admin);
$this->loadIdentityStates($states);
}
// 登陆
public function login($identity, $duration = 0) {
$id = $identity->getId();
$states = $identity->getPersistentStates();
if ($this->beforeLogin($id, $states, false)) {
$this->changeIdentity($id, $identity->getName() , $states, $identity->getAdmin());
if ($duration > 0) {
if ($this->allowAutoLogin) $this->saveToCookie($duration);
else throw new CException(Yii::t('yii', '{class}.allowAutoLogin must be set true in order to use cookie-based authentication.', array(
'{class}' => get_class($this)
)));
}
$this->afterLogin(false);
}

return !$this->getIsGuest();
}
}

在config/main.php这个配置文件中加入或更改成如下代码

1
2
3
4
5
6
<?php
'user' => array(
'allowAutoLogin' => true,
// 主要是设置这个自定义的class
'class' => "WebUser",
) ,

然后添加一个components/AdminIdentity.php文件用来处理管理员验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
class AdminIdentity extends CUserIdentity {
public $id;
public $admin=0;

public function authenticate() {
// 验证用户名是否在admins表里面存在
$admin = Admin::model()->findByAttributes(array(
'username' => $this->username
));
// 没有这个用户则返回用户名错误
if ($admin == null)
{
$this->errorCode = self::ERROR_USERNAME_INVALID;
// 验证密码
} elseif ($admin->password != md5($this->password)) {
$this->errorCode = self::ERROR_PASSWORD_INVALID;
} else {
$this->username = $admin->username;
$this->id = $admin->id;
// 设置为管理员
$this->admin = 1;
$admin->last_login_time = $admin->this_login_time;
$admin->this_login_time = time();
$admin->last_login_ip = $admin->this_login_ip;
$admin->this_login_ip = Yii::app()->getRequest()->userHostAddress;
$admin->save();
$this->errorCode = self::ERROR_NONE;
}

return !$this->errorCode;
}
public function getId() {

return $this->id;
}

// 获取admin状态 0: 不是管理员(默认值) 1:管理员
public function getAdmin() {
return $this->admin;
}
}

用于普通用户验证的UserIdentity代码跟上面的管理员类似,只是默认的 admin值变成0。然后在filters目录中添加AdminFilter.php文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
// 管理中心过滤器,过滤掉不是管理员的用户
class AdminFilter extends CFilter {
// 在action之前的过滤器
protected function preFilter($filterChain) {
if (Yii::app()->user->admin == 0) {
// 调用控制器的redirect函数,跳转到登陆页面
$filterChain->controller->redirect(Yii::app()->user->loginUrl);
Yii::app()->end();

return false;
}

return true; // false if the action should not be executed

}
}

tip

这个filters目录是我自己新建的,需要在main.php添加对应的配置。

最后就是在components文件夹里面添加一个AdminController.php。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
// 后台管理的控制器
class AdminController extends Controller {
// 设置默认layout
public $layout = 'admin';
public function filters() {
// 设置验证不过时的跳转地址
Yii::app()->user->loginUrl = $this->createUrl('admin/login/index');
// 加载管理中心过滤器

return array(
array(
'AdminFilter'
)
);
}
}

然后只要是管理员的全部继承自这个AdminController,这样就能过滤掉全部的 普通用户了。这种方法只是普通的用户过滤,如果需要详细的权限设置可以使用 一下Yii的rbac。这里就不作详细介绍了。

最近要经常将img写入到tf卡中,所以用到了dd命令,而一个镜像有4G写入很长时间,这时候就非常需要看到进度信息了。在网上找到普遍的做法都是给dd命令发送USR1信号,但在mac下面发送这个后dd进程就直接退出了,可能是版本的问题吧。后来看了一下dd的man,里面有提到可以用stdin来代替if,那么显示进度就容易了,配合上pv命令就行。

1
pv -cN source<if.img|dd of=/dev/disk2

这样就会显示出进度,速度,总传输量和剩余时间。

1
source:  545MiB 0:06:16 [1.46MiB/s] [==>                    ] 14% ETA 0:36:06

由于最近买了一块cubieboard A10的开发板,想在上面搞点事,需要用到ad-hoc 网络,所以就有了今天发现的这个奇怪的问题。

我买这个cb板子的时候一起买了一张水星MW150US,到手后发现芯片是8188eu的, 就去找了一个cubian系统,里面已经集成好这个驱动了,这几天用这个系统玩的 还挺嗨的,基于debian7的,postgresql都已经让我编译进去了。可惜今天确碰 到ad-hoc死活无法开启的问题。从deian的wiki上找来的资料里面写的配置是写 在interfaces这个文件里面的,内容如下

1
2
3
4
5
6
7
auto wlan0
iface wlan0 inet static
address 192.168.1.1
netmask 255.255.255.0
wireless-channel 1
wireless-essid MYNETWORK
wireless-mode ad-hoc

但这个保存并启用后在手机和mac上始终无法看到自己配置的那个essid,也就是 说压根没有正常启动。后来找了一个方法使用cli方法,具体的就是使用 iwconfig命令。

1
2
3
4
5
6
ifconfig wlan0 down
iwconfig wlan0 mode ad-hoc
iwconfig wlan0 essid MYNETWORK
iwconfig wlan0 channel 11
iwconfig wlan0 key 1234567890
ifconfig wlan0 up

在ubuntu的wiki上看到的也是这种方法,但比较奇怪的是在我这边也是完全没有 作用,还有出现没有操作权限的提示。折腾了很长时间,在google上找了半天也 没找到明确的说明。后来不得己的情况下改装了官方的ubuntu server,这个是 直接刷到nand里面的。刚装完的时候网卡没有配置,sshd也没有装,后来我是通 过在tf卡里面装了一个带ssh,并且已经配置上网卡的系统进去,然后挂载一下 nandc,再把里面的/etc/rc.local做一下修改,自动装上openssh-server,还有 就是添加上网卡的配置信息,这样重起一下后就可以连接了。

最后发现先要把mode设置为master,然后再成ad-hoc方式,再设置essid才能成功。

1
2
3
4
5
6
ifconfig wlan0 up
ifconfig wlan0 down
iwconfig wlan0 mode Master key off
iwconfig wlan0 mode ad-hoc key 1234567890
iwconfig wlan0 mode ad-hoc essid MYNETWORK key 1234567890
ifconfig wlan0 up

我想这个可能是网卡启动的时候直接设置ad-hoc没能将mode正确的改过来的关系吧。

起因

由于最近跟同学合作一个项目,需要在android上开发一个应用,但我不熟悉java,而且我想让这个开发出来的东西以后能方便的移植到ios上面,所以就考虑了跨平台的titanium。

无法在button上显示图片的问题

alloy的tss失败

原先我是使用的alloy框架来写这个demo,然后用下面的这段代码添加图片到按 钮上,并在浏览器中测试正常。

1
2
3
4
5
6
"#button":{
backgroundImage: "button.png",
top:10,
width:88,
height:25
}

上面这段是tss文件的内容。但这段代码编译到apk后在小米手机上运行就无法显 示出按钮上的图片了。一直没想明白是什么原因。

classic版本1

改成用js来写这段样式后的代码如下:

1
2
3
4
5
6
7
8
9
10
var btnTest = Ti.UI.createButton({
backgroundImage : 'button.png',
backgroundColor : '#0098db',
borderRadius : 5,
color : '#8F9191',
height : 25,
width : 88,
top : 10,
left : 10
});

然后效果跟上面的版本一样,也是浏览器中可以正常运行,android手机上的显 示就有问题。

最终的解决方案

最后在官方的一个其它的问题中发现了我可能是路径的设置有问题。将代码改成 如下方式后就完全正常了。

1
2
3
4
5
6
7
8
9
10
var btnTest = Ti.UI.createButton({
backgroundImage : '/images/button.png',
backgroundColor : '#0098db',
borderRadius : 5,
color : '#8F9191',
height : 25,
width : 88,
top : 10,
left : 10
});

原因应该是在android中的资源目录的路径应该是要写成从 / 开始。所以我上 面的这段路径对应的就是如下图所示的位置。

路径图

本来想今天夜里起来看看 wwdc 的,结果闹钟没把我弄醒直接一觉睡到天亮。起来看时间七点多,马上打开电脑看一下 wwdc 上面到底发布了什么。在太平洋上看了相关的介绍,没看到我想要的产品,比如新版的 Air 提升了性能和续航能力,但没有装上 Retina 屏。没有发布新版的 MacBook Pro ,到是有个 Mac Pro 的新版本非常不错,全黑的身躯,极其强劲的性能,可惜就是太贵了,上不起。然后就下定决心直接上X宝买港行的 MacBook Pro 13 Retina 2013年初的版本,下单后直接去他们店里自提。型号是 ME662,硬盘256G,吼吼~,性能非常强劲。尤其是这显示屏啊,这字体的渲染效果真棒,边缘清晰,编程的时候看那些字就没那么累了,看文字版的pdf也很劲力。

在下载软件的时候发现了一个问题,两台笔记本同时用 wifi 连接我的那个破腾达路由器的时候,只要流量一上去其中的一台就会断线,刚开始一直是 MacBook Pro 断线,后来变成了一直是神舟断线,看来问题还是出在我的那个路由器上。最后不得不用移动硬盘来传输数据,USB3.0的硬盘传输很快,很给力。

最后附上一张界面截图╮(╯▽╰)╭:

波段图

查看原图

废话就不说了直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"reflect" // 这里引入reflect模块
)

type User struct {
Name string "user name" //这引号里面的就是tag
Passwd string "user passsword"
}

func main() {
user := &User{"chronos", "pass"}
s := reflect.TypeOf(user).Elem() //通过反射获取type定义
for i := 0; i < s.NumField(); i++ {
fmt.Println(s.Field(i).Tag) //将tag输出出来
}
}

通过获取 tag 我们可以做到对 struct 进行扩展定义,像 qbs 里面就使用了这种方法。

在opensuse12.3中默认root是不能打开带有图形的程序的,会提示没有设置display环境变量。在root的.bashrc中加入如下代码:

1
export DISPLAY=:0

然后通过下面这命令给root用户加上图形连接的权限。

1
xhost local:+root

然后再登陆root就可以打开带有图形界面的程序了。

先把 kernel-desktop-devel 这个包安装上去,还要装上gcc和make,这时用下面这条命令生成一个软链接。

1
2
cd /lib/modules/`uname -r`/build/include
sudo ln -s generated/uapi/linux .

然后再用平时安装 vmware tools 的方法安装就可以找到 linux-header 了。

最近被 kafka 折腾了很久,还是资料太少了,在 python 里面连接 kafka 的库也总是有各种 bug 。连接 kafka 的库有两种类型,一种是直接连接 kafka 的,存储 offset 的事情要自己在客户端完成。还有一种是先连接 zookeeper 然后再通过 zookeeper 获取 kafka 的 brokers 信息, offset 存放在 zookeeper 上面,由 zookeeper 来协调。

我现在使用 samsa 这个 highlevel 库

Producer示例

1
2
3
4
5
6
7
from kazoo.client import KazooClient
from samsa.cluster import Cluster
zookeeper = KazooClient()
zookeeper.start()
cluster = Cluster(zookeeper)
topic = cluster.topics['topicname']
topic.publish('msg')

\ Consumer示例\

1
2
3
4
5
6
7
8
9
from kazoo.client import KazooClient
from samsa.cluster import Cluster
zookeeper = KazooClient()
zookeeper.start()
cluster = Cluster(zookeeper)
topic = cluster.topics['topicname']
consumer = topic.subscribe('groupname')
for msg in consumer:
print msg

tip

consumer 必需在 producer 向 kafka 的 topic 里面提交数据后才能连接,否则会出错。

在 Kafka 中一个 consumer 需要指定 groupname , groue 中保存着 offset 等信息,新开启一个 group 会从 offset 0 的位置重新开始获取日志。

kafka 的配置参数中有个 partition ,默认是 1 ,这个会对数据进行分区,如果多个 consumer 想连接同个 group 就必需要增加 partition , partition 只能大于 consumer 的数量,否则多出来的 consumer 将无法获取到数据。

本来想在rails中使用ElasticSearch做为全文搜索的,但对27W的数据建立索引后发现索引文件比原始数据还大。原始数据存在postgresql中,大小为1.8G,索引到ElasticSearch后却有2G,这还是不存储_source的情况下,不然更大。然后就去试了一下solr,solr在rails3里面可以用sunspot。从文档里看起来使用非常简单,直接整合到ActiveRecord,而且默认自带一个solr3.5,但不支持中文分词,所以需要自己加上。我这里使用了IK,mmseg的方法应该也类似。

下载IK Analyzer

在这里我是下载了 IKAnalyzer2012_u6.zip。

解压到solr

我是用rvm安装的ruby,所以sunspot带的solr路径是 /home/chronos/.rvm/gems/ruby-1.9.3-p374/gems/sunspot_solr-2.0.0/solr/ 。

将里面的 IKAnalyzer2012_u6.jar 解压到solr的 /home/chronos/.rvm/gems/ruby-1.9.3-p374/gems/sunspot_solr-2.0.0/solr/solr/lib 目录下面,如果没有lib目录就自己建一个。别解压到 /home/chronos/.rvm/gems/ruby-1.9.3-p374/gems/sunspot_solr-2.0.0/solr/lib 下面,我刚开始也一直以为是那个目录,然后就一直提示出错。

配置IK分词

找到 schema.xml 更改其中的 fieldType。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<fieldType name="text" class="solr.TextField" omitNorms="false">
<analyzer type="index">
<tokenizer class="org.wltea.analyzer.solr.IKTokenizerFactory" useSmart ="false"/>
<!-- <tokenizer class="solr.StandardTokenizerFactory"/> -->
<filter class="solr.StandardFilterFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.PorterStemFilterFactory"/>
<filter class="solr.RemoveDuplicatesTokenFilterFactory" />
</analyzer>
<analyzer type="query">
<tokenizer class="org.wltea.analyzer.solr.IKTokenizerFactory" useSmart ="false"/>
<!-- <tokenizer class="solr.StandardTokenizerFactory"/> -->
<filter class="solr.StandardFilterFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.PorterStemFilterFactory"/>
<filter class="solr.PositionFilterFactory" />
<filter class="solr.RemoveDuplicatesTokenFilterFactory" />
</analyzer>
</fieldType>

这里的 useSmart 是不使用智能分词

启动Solr

1
sunspot-solr start -d data

data是指索引数据存放的位置,详细的使用方法请看 sunspot-solr –help 。

在Rails3中使用sunspot

添加到rails

1
rails generate sunspot_rails:install

执行完上面的命令后会在config文件夹里生成一个 sunspot.yml ,配置里面的选项,选项很简单就不在这里列出来了。

更改Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Post < ActiveRecord::Base
searchable do
text :title, :body
text :comments do
comments.map { |comment| comment.body }
end

boolean :featured
integer :blog_id
integer :author_id
integer :category_ids, :multiple => true
double :average_rating
time :published_at
time :expired_at

string :sort_title do
title.downcase.gsub(/^(an?|the)/, '')
end
end
end

只要在Model里面添加searchable段的内容就可以了,具体的字段配置根据自己的实际数据结构和需求调整。

重建索引

1
rake sunspot:reindex

执行上面的命令就可以自动的重建索引,我这里重建完索引后的最终大小是920.3MB。

tip

Gemfile 文件里面添加 gem ‘progress_bar’ 并 bundle install 一下,然后再执行上面的命令时可以显示索引进度。

sunspot 的详细用法可以看它的文档