魑魅魍魉 发布的文章

实在是有点儿累啊,腰有些酸痛,不过即使到了现在,也还是有很多事情没有忙完,老婆的车晚点了三个多小时,最后搞到三点多才到家,肯定是很累的啊,我也是从昨天上午九点开始干活,一直到现在才算基本上搞完,这个和效率好像没有多大关系,只是事实确实是太多了。

这个星期还需要去井坡子的交通大厅交身份证,1号路考,搞完这个就OK的可以拿证了吧,所以,还是希望能一次性过啊,不想有个第二次,尤其是在前面几个科目都是一次性满分通过的情况下更不想最后一个科目来个补考,那就真的是不爽了。

现在的收入啊,有点低呢……得想法子

现在的事业啊,还没成功……得努力啊

我发现我不是一个细心的人,有的时候有点儿想得太多,有的时候又完全是什么都不想,还不像一个成功人士(我的意思不是说像一个已经成功的人士,而是像一个能成功的人士),时间浪费很严重……需要努力改正……

又在学习一门新语言 Go 了,不过我也不知道我现在还需要去学语言干嘛,除了计算机编程语言,我还想学好法语,再学西班牙语,都是很喜欢的语言啊。

朋友 胡潇 今天让我做了一个类似于百度地图、谷歌地图的UI界面的简单实现,抽了点时间给他整了一个,没有进行过什么美化,只是把基本他需要的功能做了出来,写了一点点JS实现的,JS文件map.js 的代码:

//计算文档的可见高度

function windowHeight() {
    var de = document.documentElement;
    return self.innerHeight||(de && de.clientHeight)||document.body.clientHeight;
}

function hasClass(el, name) {
   return new RegExp('(s|^)'+name+'(s|$)').test(el.className);
}

function removeClass(el, name)
{
   if (hasClass(el, name)) {
      el.className=el.className.replace(new RegExp('(s|^)'+name+'(s|$)'),' ').replace(/^s+|s+$/g, '');
   }
}

function toggleTool() {
    var tools = document.getElementById("tools-wrapper");
    var map = document.getElementsByClassName("inner")[0];
    var tw = tools.offsetWidth;
    if(hasClass(tools, "toggled")) {
        removeClass(tools, "toggled");
        map.style.marginLeft = "20em"
    } else {
        tools.className += " toggled";
        map.style.marginLeft = "0em";
    }
}

//window.onresize是窗口改变大小的时候,因为窗口改变大小,文档的可见高度或宽度会变化。
window.onload=window.onresize=function(){
 var wh=windowHeight();
 document.getElementById("tools").style.height = document.getElementById("map").style.height =
  (wh-document.getElementById("header").offsetHeight-document.getElementById("navigator").offsetHeight)+"px";
}

HTML结构很简单

body
    #header
    #navigator
    #dashboard
        .columns
            #map-wrapper.main
            #tools-wrapper .sub

然后就使用JS去控制上面这些个东西即可,我使用的是负外边距的方式来实现左右分栏,其实后来想想,JS应该可以更简单一些,比如不需要使用JS直接对 #map 的样式进行操作,只需要 #toggle-button 点击之后,给 .columns 或者 #dashboard 加一个类 .toggled 即可,然后其它的工作就全部都是CSS的事情了。

大小自适应则完全使用JS实现了,也就是网页可视区域的高度减去 #header 与 #navigator 的高度即可,如果再在加上一个 #footer或者其它的占整个网页高度的东西的话,再减去这个新加的东西的高度即可,比如我如果加了一个 #footer 在最下面,那么改变高度的函数就成了:

window.onload=window.onresize=function(){
 var wh=windowHeight();
 document.getElementById("tools").style.height = document.getElementById("map").style.height =
  (wh - document.getElementById("header").offsetHeight - document.getElementById("navigator").offsetHeight) - document.getElementById("footer").offsetHeight +"px";
}

HTML 文件 index.html :

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <title>Map</title>
        <link charset="UTF-8" href="style.css" rel="stylesheet" />
        <script src="map.js" charset="UTF-8" type="text/javascript"></script>
    </head>
    <body id="map-page">
        <div id="header"></div>
        <div id="navigator">
            <p>
                <a href="#" title="#">One Key Service</a>
                <span><label>Tel.</label><span>18374888188</span></span>
                <span><label>User Info.</label><span>China Mobile</span></span>
            </p>
            <ul id="global">
                <li>
                    <a href="#" title="#">Directions</a>
                </li>
                <li>
                    <a href="#" title="#">Travel</a>
                </li>
                <li>
                    <a href="#" title="#">Location</a>
                </li>
                <li>
                    <a href="#" title="#">Marketing</a>
                </li>
                <li>
                    <a href="#" title="#">Locate</a>
                </li>
                <li>
                    <a href="#" title="#">Behavior Statistics</a>
                </li>
                <li>
                    <a href="#" title="#">Real-time Traffic</a>
                </li>
            </ul>
        </div>
        <div id="dashboard">
            <div>
                <div id="map-wrapper">
                    <div>
                        <!-- <iframe src="http://cox.antusoft.com" id="map"></iframe> -->
                        <div id="map">
                            <div id="map-demo">
                                Your Map Content Here!
                            </div>
                        </div>
                    </div>
                </div>
                <div id="tools-wrapper">
                    <div>
                        <div id="tools">
                            <a href="#" onclick="toggleTool()" title="Toggle" id="toggle-button">Toggle</a>
                            <div>
                                <form action="#" method="get">
                                    <h2>Search</h2>
                                    <div>
                                        <ul>
                                            <li>
                                                Bus
                                            </li>
                                            <li>
                                                By Car
                                            </li>
                                        </ul>
                                        <div>
                                            Your content here!
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
                <div></div>
            </div>
        </div>
    </body>
</html>

CSS 文件 style.css :

/* http://meyerweb.com/eric/tools/css/reset/
   v2.0 | 20110126
   License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
    display: block;
}
body {
    line-height: 1;
}
ol, ul {
    list-style: none;
}
blockquote, q {
    quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
    content: '';
    content: none;
}
table {
    border-collapse: collapse;
    border-spacing: 0;
}

/** common **/
body {
    font-size: 80%;
    line-height: 1.5em;
}

a {
    text-decoration: none;
}

.clear {
    clear: both;
}

.section {
    width: 100%;
}

/** .columns **/
.columns {
    width: 100%;
}

.columns .main {
    float: left;
    width: 100%;
}

.columns .sub {
    float: left;
    width: 20em;
    margin-left: -100%;
}

.columns .main .inner {
    margin-left: 20em;
}

/** .nav **/
ul.nav,
ol.nav {
    letter-spacing: -0.31em; /* webkit: collapse white-space between units */
    *letter-spacing: normal; /* reset IE < 8 */
    word-spacing: -0.43em; /* IE < 8 && gecko: collapse white-space between units */
}

ul.nav li,
ol.nav li {
    display: inline-block;
    zoom: 1; *display: inline; /* IE < 8: fake inline-block */
    letter-spacing: normal;
    word-spacing: normal;
    vertical-align: top;
}

ul.nav a,
ol.nav a {
    display: block;
    padding: .5em;
}

/** #navigator **/
#navigator p.nav {
    line-height: 2em;
    border-bottom: .1em solid #666;
}

#navigator ul.nav {
    border-bottom: solid .3em #55aed7;
    color: #bbb;
    background-color: #666;
    background-image: -khtml-gradient(linear, left top, left bottom, from(#777), to(#666));
    background-image: -webkit-gradient(linear, left top, left bottom, from(#777), to(#666));
    background-image: -webkit-linear-gradient(#777, #666);
    background-image: -moz-linear-gradient(#777, #666);
    background-image: -o-linear-gradient(#777, #666);
    background-image: linear-gradient(#777, #666);
}

#navigator ul.nav a {
    color: #bbb;
    padding: .5em 1em;
}

#navigator ul.nav li.active {
    color: #f0f0f0;
    background-color: #55aed7;
    background-image: -khtml-gradient(linear, left top, left bottom, from(#6ebce0), to(#55aed7));
    background-image: -webkit-gradient(linear, left top, left bottom, from(#6ebce0), to(#55aed7));
    background-image: -webkit-linear-gradient(#6ebce0, #55aed7);
    background-image: -moz-linear-gradient(#6ebce0, #55aed7);
    background-image: -o-linear-gradient(#6ebce0, #55aed7);
    background-image: linear-gradient(#6ebce0, #55aed7);
}
#navigator ul.nav li.active a,
#navigator ul.nav a:hover {
    color: #f0f0f0;
}

/** #dashboard, #main **/
#tools {
    position: relative;
    border-right: .5em solid #ccc;
    box-shadow: 1px 1px 3px #333;
}

.toggled {
    width: 0px !important;
}

.toggled .tool {
    display: none;
}

#toggle-button {
    position: absolute;
    height: 3em;
    width: 1em;
    top: 50%;
    left: 100%;
    margin-top: -1.5em;
    background-color: #ccc;
    border-radius: 0 .5em .5em 0;
    box-shadow: 1px 1px 3px #333;
    text-indent: -9999em;
}

.toggled #tools {
    border-right-color: #999;
}

.toggled #toggle-button {
    background-color: #999;
    box-shadow: 1px 1px 3px #333;
}

#map {
    width: 100%;
    border: none;
    overflow: auto;
}

div#map {
    background-color: #ddd;
    background-image: -khtml-gradient(linear, left top, left bottom, from(#eee), to(#ccc));
    background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#ccc));
    background-image: -webkit-linear-gradient(#eee, #ccc);
    background-image: -moz-linear-gradient(#eee, #ccc);
    background-image: -o-linear-gradient(#eee, #ccc);
    background-image: linear-gradient(#eee, #ccc);
}

/** Demo Content **/
div#map {
    position: relative;
}

#map-demo {
    position: absolute;
    width: 20em;
    height: 3em;
    margin-top: -1.5em;
    margin-left: -10em;
    top: 50%;
    left: 50%;
    text-align: center;
    font-size: 3em;
    font-family: Arial, Helvetica, sans-serif;
}

这个问题似乎对于我一个本身是英语专业的原计算机从业人员来讲,是有点复杂,从我10年10月回湖南开始,做服装这一行也有整整两年的时间了,我自我感觉有了很大的进步,回顾一下最开始的我状态是一问三不知,那个时候我不知道服装零售行业的半点儿知识,唯一知道的也都是任何一个正常人都能想到的,那就是去批发商那里进货,以某种方式计算一个零售价,然后零售给最终客户,但是现在却越来越发现,作为一个好的服装生意人,这里面的道道还是很多的,比如我感觉对我来说最难的就是跟踪的有零售店里面的货品的销售及调配了。

做服装零售,以这两年的所见所得,感觉要赚更多的钱,有下同这些问题需要解决:

  • 尽可能的减低固定成本开支(房租,水电等等),这笔开支是不管店了营业营业都会有的。
  • 尽可能的降低货品成本(这个就得在进货的时候讲价,越来越发现,讲价也是一门技术,更可以说是一门艺术)
  • 减少在职工身上的开支(这个我们的做法其实相反,我们是愿意给职工发高工资的,但高工资的前提是她能带来更高的销售业绩,我们可以说是全市提成点最高的零售店了)
  • 要有好的商品
  • 尽可能的销售更多的货品同时又要尽可能的减少货品的积压
  • ……

可能还有一些其它的问题需要注意,但是我感觉上面这一些能足够构成是否能赚钱的前提了,我做为一上三非人员(或者说什么都是吧,因为我确实是什么事情都要做),做得最多的还是上面说的最后面这一项,就是如何尽可能地减少货品的积压,这使得我需要有能力在尽可能短的时间里掌握下面这些信息:

  • 任何一个款式在任何一个门店里面的库存量;
  • 任何一个款式在任何一个门店里面的销售情况(最尽三天、七天、十天……)
  • 哪些是门店中可返回批发商处退换货品、它们的退换期限、距离退换期限时间、退至批发商所需要的物流时间等等
  • 哪些货品是已经补个过的不允许退换的(如果某一个款式有多个颜色,那么未补过货的颜色是可以退换的)
  • 任何一款货品还能销售多长的时间(季节性商品只有在它所属的季节销售,过季要么打折,要么下帐下一次再销售)
  • 任何一款衣服所有门店+仓库的库存总量、日平均销售量等
  • 任何一款衣服每一个颜色每一个尺码的销售量、库存量等信息
  • 任何一款衣服的上架时间
  • ………………

还有很多我也不知道如何去说的信息,只是所有这些信息只是需要我采集得到,然后再需要将这些信息转换成下一步的工作,该拿去退换的拿去退换,该在门店之间调配的就要调配,比如某一款衣服的黑色 L 码在 一店已经卖断 ,但是在三店却只卖了一件XL,五店也只卖了一件 2XL,那么我可以把三五店的L码先设调到一店,然后再看一两天的销售情况,如果一店再卖断,那么就需要适当的补货,补货需要考虑颜色,尺码,可销售时间等等,在这个观察期内,我们还需要关注其它门店的销售情况是否有所好转等等。

上面只是最简单的对一个款式的跟踪,我真正的工作,需要把上面那样简单的事情扩大几百倍吧,因为我需要同时跟踪数百个款式,每一个款式又有多个颜色、多个尺码,它们来自多个供货商,每一个供货商的可退换货时间是不同的,地点也是不一样的,退货物流时间也是不一样的……我现在使用的还是最笨的方法来完成这样的任务:

每天在门店日结之后,将其所销售的货品号抄下来,然后在进销存查询中去专门查询这些款式的销售情况,再决定调配,然后周日、周二、周四的晚上需要把所有在售的款式(不管是以前积压的还是新近的款式)都查一次进销存,尤其是周五(我需要在这一天把货品调一次,让每一个货品都能在周末展示在卖得好的那个门店里面)查询到了的结果我也是一个一个的抄下来,比如下面这样的:

|款号|颜色|数量|调配| |–|–|–|–| |102022|黑|XL/2; 2XL/1|三 -> 五| |102022|红|L/1|一 -> 六| |102023|杏|S/1,M/2,L/1|仓 -> 二|

这样一个一个抄下来,然后第二天在门店上班的第一时间打电话给店子一个一个的通知,中午再由一个人把所有货品去取、发到各个门店(这个成本有点高,但是现在也还没有想到别的降低这个成本的办法),只是有一个,我可以把商品的移仓单(门店至门店的调货单据)全部开好,然后店员只需要对照单据发货即可,但是因为以前遗留下来的问题太多,比如几乎所有店员都习惯于以前的手工单据,她们会把销售人顾客的服装款号及实际售价抄在本子上,然后再在某个时间输入到电脑上面,这样一来,客人没有详细的票据,而且因为颜色尺码都是靠得记忆,很有可能出错,所以,店里面的商品库存又有可有是不正确的(事实证明有很多都是不正确的,比如卖的是黑色M码,但是开的单据却是黑色L码)。

这些遗留下来的问题让我的工作难上加难,所以,我还需要定期的对商品进行盘点,而盘点的最主要作用就是恢复每一款商品每一个颜色尺码的正常库存数以方便货品的跟踪,但是我会发现,不出一个星期就会有很多商品的库存又出错了。而这还并不是唯一的问题,还涉及到金钱上的,因为遗留下来的问题之一是允许顾客议价,所以,1000元的衣服,在A手上卖出去可能是990元,在B手上卖出去可能是900元,因为我们有一个最低允许折扣,在这个折扣之上,对于店员来说是自由的,那么,如果卖出去的时候顾客支付了990元,等到店员开单的时候可能只开900元,这个,如果店里面没有收银员的话是可以的,而且如果店员与收银员“合作”的话,几乎任何时候都可以这么做。

问题多多,最难的还是遗留问题,但是这些应该是传统转向现代方法过程中都会遇到的问题吧,只是我还无法去解决它……或者说还没有找到最好的解决办法。

不知不觉中,宝贝女儿潘艾已经一周岁了,一岁生日有学校所有二十多个小朋友的陪伴,奶奶给买了一个大蛋糕,我也和老婆一起回来了,让人最开心的事情是五十多天没见我们,一见着我们就扑了过来。看那个大黑袋子是什么?那是给潘艾同志滴玩具哈,因为新鲜事物对她来说,过时得太快了,只好一次性多买些花样,然后时不时的拿出一个给她玩,玩腻了再换一个没玩过的。

女儿一岁照
女儿一岁照-点击照片查看一岁生日相册

折腾无极限啊,从06年开写玩博客,最开始使用的是Z-Blog,之后换成Bo-Blog,然后就用了几年的WordPress,再又是Drupal,再又是TextPattern,之后上一次不知道是什么原因又转成了WordPress,然后这一次也不知道是什么原因让我冲动的又转到了TextPattern,可能是看到TextPattern破天荒的进行一次小版本升级?

好吧,其实我想可能原因是因为这一次TextPattern的默认主题不再那么“简单”了,至少看上去像个样子了,对于我这种懒人来说,那就太OK了,再加上,WordPress越来越多的功能是我不需要的,所以,我还是回到最适合我的TextPattern上来吧。

把TextPattern的文本格式化改成了 MarkDown(Textile还是不太适合我啊),修改方法见 在 TextPattern 4 中使用Markdown ,另外,还做了一个简单的后台主题,如下图:

UPDATE: 下图个屁啊,图片都被删除了,一不小心把整个网站都删除了,还好数据库还在

颜色和样式模仿的是 OutLook.com 的,我太喜欢它这种简单的风格了,准备哪天有时间最主要是哪天有心情的时候把前台也换成那个样子。

如果你也想从WordPress转到TextPattern的话,TextPattern提供了一个导入工具,只需要提供你现在的WordPress的博客的数据库信息即可,但是有几个要注意的地方:

  • 在WordPress 里面的所有内容分类和链接分类都会导入进来
  • 在WordPress 中的标签在TextPattern全部会导入为分类(如果你的标签很多的话,你会发现导入之后TextPattern里面就有那么多个分类)
  • TextPattern里面好像没有Tag功能(我一直没去在意这个东西)
  • WordPress的文章图片会导入成为一个完整的URL地址
  • WordPress 媒体库里面的内容是无法导入过来的,要么保留原来的目录结构,要么就是手再上传或者你可以自己再写一个导入工具)

我在导入之前把以前的博客的数据库备份了一下,然后把所有的标签都删除了,把所有的文章分类也都删除了,越简单越好,毕竟,我感觉有一个搜索功能就成了。

TextPattern 是一个十分不错的博客工具,也是我一直使用的工具,与另一个很好的博客工具WordPress比起来,TextPattern清新许多,它所有的,都是我所要的,没有任何多余的在里面,唯一的一个缺点就是它没有提供Markdown插件(或者说它只提供了Textile支持),而我一直使用的都是Markdown,所以,还得自己整一下。

首先需要下载两个必须的文件:markdown.php 以及 smartypants.php ,它们的下载地址是:

之后修改 ./textpattern/lib/classTextile.php 为 ./textpattern/lib/classTextile.php.bak 对原 classTextile.php 作一个备份,之后将下载并解压后得到的 smartypants.php 与 markdown.php 两个文件移动到 ./textpattern/lib/ 目录中,并修改 markdown.php 为 classTextile.php。

安装之后,需要让 markdown 真正以 textile 的方法运行,我们还需要修改当前的 classTextile.php 文件:

打开 classTextile.php 后搜索 “### Textile Compatibility Mode ###” ,可以看到如下代码:

if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
    # Try to include PHP SmartyPants. Should be in the same directory.
    @include_once 'smartypants.php';
    # Fake Textile class. It calls Markdown instead.
    class Textile {
        function TextileThis($text, $lite='', $encode='') {
            if ($lite == '' && $encode == '')    $text = Markdown($text);
            if (function_exists('SmartyPants'))  $text = SmartyPants($text);
            return $text;
        }
        # Fake restricted version: restrictions are not supported for now.
        function TextileRestricted($text, $lite='', $noimage='') {
            return $this->TextileThis($text, $lite);
        }
        # Workaround to ensure compatibility with TextPattern 4.0.3.
        function blockLite($text) { return $text; }
    }
}

修改为:

if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
    # Try to include PHP SmartyPants. Should be in the same directory.
    @include_once 'smartypants.php';
    # Fake Textile class. It calls Markdown instead.
    class Textile {
        function TextileThis($text, $lite='', $encode='') {
            if ($lite == '' && $encode == '')    $text = Markdown($text);
            if (function_exists('SmartyPants'))  $text = SmartyPants($text);
            return $text;
        }
        # Fake restricted version: restrictions are not supported for now.
        #function TextileRestricted($text, $lite='', $noimage='') {
        #   return $this->TextileThis($text, $lite);
        #}
        # Workaround to ensure compatibility with TextPattern 4.0.3.
        function blockLite($text) { return $text; }
        function TextileRestricted( $text, $lite='', $encode='')  {
            return $this->TextileThis($text, $lite, $encode);
        }
    }
}

现在已经可以直接在编辑内容时使用 Markdown 语法了,当然,这样一来,不能再使用 Textile 语法,不过有一种想法,修改函数 TextileThis() ,让 $text 通过真正的 Textile 函数处理一次再返回应该是没问题的,不过如果 Textile 与 Markdown里面有相同的标记语法但有不同的解释的话,就不成了。

FastCGI是一种最常见的在如Nginx,Lighttpd和Cherokee等服务器上部属Flask应用的方法,当然,首先得先有一个FastCGI服务器,使用最多的就是Flup。

创建一个 .fcgi 文件

最开始需要创建一个FastCGI服务器文件,比如我的一个应用为costony,那么我可以把这个文件叫作: costony.fcgi :

#!/usr/bin/python
from flup.server.fcgi import WSGIServer
from costony import app
if __name__ == '__main__':
WSGIServer(app).run()

如果使用的Apache服务器的话,那么上面这些就已经足够了,但是Nginx或者老一些的Lighttpd服务器的话,还需要明显地把 socket 传送给 WSGIServer。

WSGIServer(app, bindAddress='/tmp/costony-fcgi.sock').run()

上一行的Socket路径应该与服务器配置文件里面的一样,保存 costony.fcgi 文件到任何一个你还可以找得到的地方,比如 : /home/wwwroot/costony.com/ 或者其它的类似的目录,然后需要让这个文件可执行:

#chmod +x /home/wwwroot/costony.com/costony.fcgi

配置 Lighttpd

基本的Lighttpd FastCGI配置如下:

fastcgi.server = ("/costony.fcgi" =>
((
"socket" => "/tmp/costony-fcgi.sock",
"bin-path" => "/home/wwwroot/costony.com/costony.fcgi",
"check-local" => "disable",
"max-procs" => 1
))
)
alias.url = (
"/static/" => "/home/wwwroot/costony.com/static"
)
url.rewrite-once = (
"^(/static.*)$" => "$1",
"^(/.*)$" => "/costony.fcgi$1"
)

别忘记必须开启FastCGI,Alias以及Rewrite模块。

配置 Nginx

Nginx有一些不同,它默认情况是没有FastCGI参数被发送的,所以一个基本的Nginx FastCGI配置如下:

location = /costony { rewrite ^ /costony/ last; }
location /costony { try_files $uri @costony; }
location @costony {
include fastcgi_params;
fastcgi_split_path_info ^(/costony)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_pass unix:/tmp/costony-fcgi.sock;
}

上面这个配置是让应用在 /costony 这一级目录被访问,如果像我一样,把应用直接放在根目录被访问的话,我们可以像下面这样:

location / { try_files $uri @costony; }
location @costony {
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param SCRIPT_NAME "";
fastcgi_pass unix:/tmp/costony-fcgi.sock;
}

运行 FastCGI 进程

如果上面这样还不行的话,我们还需要主动运行FastCGI,运行下面这个命令:

$screen
$python /home/wwwroot/costony.com/costony.fcgi

之后我们还需要对这个Socket相应的权限:

$chmod 0777 /tmp/costony-fcgi.sock

最近这段时间新店开业,然后把仓库也从东塘搬到了火车站这边,仓库面积大了,可利用的空间也就大了,这边的房子是两层的,一楼门店二楼做仓库,在电信开了一个新户,然后老仓库的网络也搬了过来,两个四Mb的本来是想用一个多WAN路由器同时连接,但是后来却没有这么多,利用现有的一个十六口路由器,一个Model,一个TP-Link无线路由器以及一个TP-Link无线路由+Model一体机,将两个网分开来,基于路由+Model一体机的网就简单了,但是另外的一个却有点儿麻烦。

以前一直都是使用一个路由+Model一体机的网或者一个Model+一个无线路由器的网,这种是最简单的了,但是我现在的需求是:多LAN有线路由,同时还在带有一个无线WIFI热点,我的计划是下面这样的:Model连接ADSL线,有线路由器连接Model,无线路由器连接有线路由器,经过几天的实验,找到了下面两种组网方法(其实后来Google了一下,这两种方法网上早就有介绍,只是自己却一直没有去搜索,原因很简单,按着自己的想法去做确实实现了组网,就“很理性”的认为只有这一种方法。

方法一:无线路由器WAN口连接有线路由器LAN口

这种方法是最容易理解的,两个路由器的作用是一样的,连入网,并将网络分配给多台终端,配置方法如下:

  1. 恢复路由器配置至出厂设置;
  2. 先连接无线路由器(这个看你是把哪个路由器接入Model,那个不接入Model的先进行设置);
  3. 将无线路由器的设置修改为下面这样的:
    • WAN口静态IP:192.168.1.2 (只要是有线路由器的网络中未使用的IP均可)
    • WAN口子网掩码:255.255.255.0
    • WAN口网关:192.168.1.1
    • LAN口IP:192.168.0.1 (只要与有线路由器的IP不在同一网段即可)
  4. 打开DHCP服务
  5. 设置其它参数即可完成配置

这种方式,连接到有线路由器的终端与无线路由器的终端是不在同一网段的,慢慢的发现了一些问题,比如我的MACBOOK PRO不能访问Windows共享文件夹或者打印机等,后来就有了下面这种方式。

方法二:无线路由器LAN口连接有线路由器LAN口

这种方法的配置更简单:

  1. 先连接无线路由器
  2. 设置其LAN口IP地址为:192.168.1.2(与有线路由器的IP在同一网段)
  3. 关闭 DHCP服务
  4. 设置其它参数即可完成配置

这个时候,无线路由器的作用就是一个交换机,不管是连接到哪个路由器上面,IP地址都将由有线路由器分配,并且所有的终端都在同一个网段中。

以前从来没有玩过路由器,都是在虚拟机里面用得多,最主要的原因还是我从来就没有想过TP-Link的路由器除了路由还可以做交换机,或者说本来从路由器产生的那天起就是这样的?

引导语:我在美国生了两个孩子,生育前后都有培训班,家庭医生每次洗脑让我受益匪浅,我的两个宝宝在婴儿时期乖巧得好像家里没有小婴儿,我甚至疑心她们会不会哭?如今外婆常拿这句傻话笑我。回国后看到朋友或邻居们被小祖宗折磨得精疲力竭,想着将妈妈经写下来,这几年在她的指导下学到很多一辈子都受益的育儿宝典。

绝招一:如何让小宝宝不哭

爱哭的BABY都是爸妈教出来的。才出生的小BABY听不懂话,他们靠条件反射来做判断。中国的家长一听到孩子哭就抱起来。JANEGU医生反复告诫我:孩子一哭就放下来,不哭才抱起来,正好反过来。这样一来,再小的孩子都能明白,不哭的时候才有得抱,这招极灵。放下爱哭的孩子开始训练,看着时钟,不要抱让她哭,准备等10分钟再抱。第一次可以设定5分钟,以后一次比一次时间拉长,我的妞妞第一次就没有熬到5分钟,大约3分半的时候就停了,我已经心疼得肝肠寸断自己都快哭了,一定要忍住了。让孩子知道哭声是叫不来妈妈的。

训练“不哭的孩子”要排除4个哭的其他原因:尿了、病了、饿了,困了。上帝保佑,我家两个孩子都不会无理由哭,偶尔哭一下当作运动也是极少数,妞妞和妙妙100%是快乐的宝宝,躺在床上都咯咯笑出声的孩子。

绝招二:如何让刚出生的小宝宝一觉睡到天亮

美国医生再三批评我:你不是好妈妈,隔壁的孩子一睡到天亮,隔壁的妈妈是好妈妈,你不是!每次去看家庭医生我都挨批评,原因是我晚上要喂奶1-2次,抱怨养孩子很累很辛苦。医生告诉我一个观念:人类晚上不需要进食!我傻乎乎问,为什么呢?医生没办法,只好大声说:因为我们是人类!我印象极深,妞爸早就笑弯腰了。

很显然,我担心BABY晚上会饿根本是多余的,10-12小时的连续睡眠,对孩子来说比吃奶重要100倍!我们被老大妞妞折磨很惨,晚上吃一顿奶的坏习惯一直延续到快2岁。养老二的时候按照医生说的,彻底让我和老公解脱出来,从刚出生到一个礼拜我家妙妙被训练距离5个小时才吃奶,2个礼拜之后,孩子就能睡天亮了。晚上不需要吃奶!!!(三个感叹号是请妈妈们切记的意思)

绝招三:让宝宝自己静静入睡,不要摇晃

医生说,一个快乐的BABY会睡在床上唱歌的(不是真唱,是婴儿咿咿呀呀)!婴儿的大部分时光都需要在床上度过,这里请爸爸妈妈们记住,床是不会动的!所以,抱起婴儿的时候不要摇晃,不要来回走动,更不要这样哄孩子睡觉,轻轻放在床上让他自己睡就好。再说一次,床是不会动的。爸爸妈妈你们没有权利剥夺孩子躺在床上享受快乐的权利。 还有一点一定要注意的就是,兴奋的家长不要用力和孩子“疯闹”,孩子的大脑在婴儿时期还是胶状的果冻状态,很容易受伤。

绝招四:趴着睡养美女

个人经验,孩子趴着睡觉比仰着睡得安稳,才出生的孩子心脏朝下,几乎不会一惊一惊的状态,模仿子宫被包裹的状态。另一个最大的好处是改变亚洲人的大饼脸,5天之内的婴儿需要1个小时帮忙翻脸,5天之后,我家妞就会自己左右转向自如了。妞是标准的小脸,和生出来的时候大方脸好像变了一个孩子,呵呵,得意。

老二妙妙的脸没有刻意这样做,那时候妞妞需要人照顾,没有精力1个小时翻头的精力了,很可惜。这个机会只有在出生到3个月之内有效,大了就没用了。

绝招五:宝宝的抵抗力比你强十倍

爸爸妈妈穿2件,宝宝绝不能穿2件半,只能比你穿的少,不能比你穿的多。我家两个孩子都十分强壮,冷热其实很好控制,偶尔看到儿童医院发烧的孩子被花被子棉袄裹着一层一层的,可怜啊,孩子不会说话,都是家长害的。

绝招六:拒绝抗生素!

感冒发烧是孩子成长的过程,是增强抵抗力的过程。我家宝宝发烧了最常用的就是用冰敷,用有拉链的保鲜袋装冰块,用干净毛巾裹了冰敷在全身,30分钟一定退烧。烧厉害了配点美林或泰诺。如果是着凉了感冒没有发烧,就让孩子吊着鼻涕没有事,几天就好。不要相信任何药能治疗感冒。到目前为止,除了去年的手足口被医院骗进去那一次之外,我家2个孩子从来没有挂过盐水和注射过抗生素。

在美国,是那种要死的病才注射抗生素的,在中国注射室的景观,吓坏了妞妞的爸爸,那个景象让人发毛他说。一排一排的吊针,打手打头打脚的小孩子们在妈妈爸爸爷爷奶奶的陪护下挂盐水。集中营莫过如此,很恐怖。花钱不说,更可怕的是体内的细菌大战存留的各种毒越来越多,周期性的需要挂盐水来压下去,常常听到妈妈们说,有一点症状马上挂水压下去,十分得意。我为她们的无知十分惋惜。我的孩子越来越健壮,从不生病,病了也几天自己好,基本不治疗。邻居的孩子半夜跑医院,累的人仰马翻乐此不疲,从这点看我是好妈妈。

绝招七:天天洗澡,用清水,少用沐浴露

我的2个宝宝健康的另外一个原因是天天洗澡,天天洗澡是必要的。记得我生完孩子出来,准备听我妈妈的教诲躺着不动,打算熬到第二天才洗澡,护士惊讶地拉我起来洗澡“不管你什么传统,把汗水和细菌都洗掉才会健康”。我被误会成个脏妈妈被拖去洗澡,当然要说明的是我生两个都是顺产。产妇如此,何况小孩子?

孩子每天运动量大,臭汗奶腥味便便尿尿什么味道都有,一定要洗澡,不能隔天才洗。这也是健康不生病的重要原因,他们不会着凉的,抵抗力比成人强十倍。医生交代:宝宝洗澡尽量用清水,再名牌的婴儿产品都不如清水好,不要用任何沐浴露、洗发水,清水最最好!医生说尽量不用,一定要用的话一个星期用一次沐浴露就好,我家是三天用一次(我觉得一周太长了,心理作用)。

绝招八:没有奶不够吃的妈妈,只有不想喂奶的妈妈

能吃到2岁半是天下最幸福的宝宝。JANEGU医生说,没有奶不够吃的妈妈,只有不想喂奶的妈妈,孩子的食量多大,妈妈的奶就有多少,生双胞胎的妈妈的奶同样够两个孩子吃。这一点我很失败,尽管GU医生用笔画了一个圆圈,她告诉我,你的孩子吃多少,你就能生产多少,你觉得奶不够,加奶粉进去,奶就会真的不够,最后就没了,事实证明我后来真的越来越少直到没了。孩子不会说话,她吃饱没有很难知道,妈妈觉得奶少了就放弃母奶,就补奶粉,其实是告诉自己的身体,我只能生产这么多了。

要相信自己能当头称职的奶牛。如果再生一个,一定要证明一下。生完小孩第三天就去医院参加“喂奶班”,几个妈妈带婴儿围成一圈,有黑妈妈白妈妈和亚洲妈妈,喂奶课用的是那种很舒服的沙发,脚下有垫脚的专用凳子。医生们将孩子脱光,包括尿布,然后称BABY的重量精确到小数点后面2位数。然后让我们抱孩子喂奶,不准穿衣服只穿尿布。医生一对一教妈妈们如何让孩子一口就咬准奶头,如何保护奶头,一次一次训练,期间有别的妈妈来用免费提供的吸奶机下奶,用塑料袋装回家。我刚开始的害羞和尴尬慢慢没有了,呵呵,我在家完全不知道妞妞吃了多少,这下好了,一堂课下来,马上称孩子份量,我妞妞吃了不少,好像是那天的第二名,前后数据证明,我有奶。

绝招九:宝宝吃奶睡着怎么办?

BABY被妈妈抱着舒服又安全,没吃饱就睡着了,醒了又吃两口,折磨妈妈,宝宝也吃不饱睡不好,害人害己。训练班里学到最有用的一招,如何弄醒孩子让他们专心吃奶,就是脱衣服!脱得就剩尿布,医生告诉我,放心吧,你抱着她有妈妈的体温,这体温能保证婴儿不会着凉,温度刚刚好,BABY有一点凉意,他们会用心进餐(她说的真是进餐,呵呵)。

放心吧,这些看起来弱不禁风的小东西,从妈妈母体带出来的抵抗力比成人强10倍,喂奶的产妇也不容易病,这就是繁殖的力量。(当然室内的温度是有一定温暖的,国内无空调寒冷地区的人不要对照模仿误会)

绝招十:母奶是最好的“抗生素”

妞妞大概10来天的时候,一只眼睛发炎化脓了,我和妞爸吓得抱到医院,还是急诊,儿科的男医生问,你喂奶还是奶粉,我说母奶。他说挤点出来放进孩子眼睛,BABY就会好。What?我排队等急诊老半天就这么打发我回家了。于是如法炮制,母奶当药,几天后妞妞果然见好,是否母奶的原因还是不清不楚,美国医生忽悠得或许有点道理吧。总之母奶是好东西。

绝招十一:你会换尿布吗?

废话,养孩子的谁不会换尿布呢?不一定哦。你是否有被孩子喷到尿或臭便便的时候呢?一天洗5次床单是我的最高记录。换尿布的时候,每次来的新阿姨都要重新教一遍,窍门就是把干净尿布放在臭尿布下面,再打开尿布,擦干净抽出来,这样即使孩子正好那时候又便又尿,你的床单保住了。习惯这么做以后,永远没有意外发生。

绝招十二:你会拍嗝吗?

 

  • 姿势1:趴在肩膀上轻拍孩子后背,这种是传统姿势,很多时候不太容易拍出来嗝。
  • 姿势2:让孩子侧坐在腿上,左手托着孩子的脖子,撑起下巴,右手拍后背,这样效率极高,通常十几下就拍出嗝了。

临睡的最后一顿奶,一定要喂饱足了,拍完嗝继续喂,我家妙妙2礼拜就睡整晚就是用这招,拍完嗝还可以再喂2-3次,直到她真的不吃了,小嘴巴往外推奶瓶的状态,妙妙就能睡到天亮。

 

绝招十三:早点吃五谷杂粮

4个月吃米粉再大点跟大人一起吃东西,孩子长大不太会偏食,早点吃和成人一样的食物对孩子有好处。6颗牙的时候,JANEGU医生说,她已经可以用这几颗牙齿吃很多东西了,什么都可以喂,没有不能吃的。

绝招十四:别破坏宝宝的专注力

再小的宝宝玩玩具或看电视或看图画书的时候,请家里的阿姨或者任何人都别打断她们。即使是吃饭的时间到了,晚一点也没有关系。培养孩子的专注力就等于培养她们长大了能注意力集中做任何事情。我家妞妞专注力很好,她喜欢的事,比如拼图,一个下午几个钟头不抬头,没有人叫她,除了尿尿之外。最怕那种大呼小叫逗孩子的长辈们,自以为聪明,边看电视边问问题。闭嘴,请安静。给孩子一个专注的环境吧。

绝招十五:慢慢走路,小声说话

身体力行很重要,大人怎样的孩子都会学。难得遇到上海一位陆老师,教孩子慢慢走路,轻声说话,人家听得到就好了,别那么大声。回了家乡,小区的幼儿园老师教孩子们扯起嗓门唱歌,我在一旁看,心想完了,培养了半天的小淑女被汉派文化同化了,哈哈,如今的妞妞热干面一样的个性十足,整个儿一辣妹。大环境很厉害,几乎没有不被同化的可能。

绝招十六:只要没有生命危险,鼓励宝宝试试看,摸摸看

中国父母礼节有余,胆子太小。“别动,危险,站起来地上脏,别摸,会刺到。。。。不许这样,不许那样”美国父母最常说的却是“Tryit!”试试看,摸摸看。鼓励宝宝,只要没有生命危险,不要阻止孩子用他们的小手尝试和认识世界。大不了衣服弄脏,地上滚来滚去的宝宝时光很短,饭粒菜汤翻了一地,大不了扫一下。他们会早学会吃饭甚至用筷子,玩水大不了感冒,玩沙子大不了进鞋子难受难清洁。总之有很多阻碍孩子快乐的条条框框,促使我们的小天使们越来越快地变成成人,变成听话和懂事的孩子。

快乐第一重要,衣服脏了不重要,只要没有生命危险,不要阻止你的孩子。

绝招十七:也是最重要的一招,一天只能批评两次!

父母也不可以随便批评宝宝,一天只能批评两次,两次用完了就等明天的配额吧。美国老师这么慎重交代的。于是,宝宝外婆说:“你的孩子一天10次都不够,2次的话我10分钟就用完了,这太难了。我大笑。在婴儿的时期,批评有用吗?当拉个便便也被阿姨批评“宝宝又干坏事了”时,我会反问阿姨,你有哪天不拉屎吗?

编后语:除了照顾好孩子的生活,更重要的是培养好孩子的习惯和性格,要与孩子讲道理,多沟通,孩子 的性格先天也有部分,所以不同的孩子也要有不同的教育方法,因人而异,不要和别的孩子比,特别不要老挂在嘴边。或许可以帮到被小祖宗弄得手忙脚乱、身心疲惫的年轻爸爸妈妈。

好吧,我知道我很傻,最主要的原因还是因为自己的各种不爽,先是WordPress越来越不简单了,如果要复杂,我有Drupal了,我只想有一个程序可以写写博客,但是,我可能有各种控的原因吧,总是感觉不爽,比如,我从TextPattern导入进来的内容一团乱,因为TextPattern使用的是Textile格式的,和WP不同,另外,我还是一个ID控,不习惯于WP没几下子就搞出几百号ID出来,还有,我喜欢编写界面足够的简单,等等的各种各样的问题让我最终决定,还是自己完全按照自己的想法来开发吧。

程序的预览版本可以在 dev.crabison.com 看到,今天才是开发的第二天,我所想要的最基本的功能也已经有了,无非就是增加、删除、修改和查看文章贝,这些都差不多了,只不过太久没写代码了,代码可能还一团乱,也没有什么特别的计划之类的,想到什么写什么,同时,昨天还花了近一个小时写了一个脚本,可以从WP或者 TextPattern上面导入数据,我只需要文章和标签数据,另外,因为系统的文件管理功能还没有写,所以,现在还无法导入文件(图片等)。

我对于这个系统的想法是这样的:

  • 能管理自己的日记、文章、相册、视频、代码、项目以及静态页面
  • 文章、日记、相册、项目都是基于某一个节点,也就是上面所说的这些所有的内容类型其本质上都是一个节点,这样方便我的管理
  • 文章和日志差不多不需要任何其它的修改,直接就按节点的数据结构即可
  • 图片使用图片集的方式组织,即上传任何一张图片都必须至少属于某一个图片集,也就是相册,同时图片还是一个文件,所以对于图片、视频或者项目文件等都是一个文件,有一个文件系统专门来管理,我希望的是所以文件我上传上去之后可以以最简单的方式访问,所以,我计划是以库的形式来组织,这样,我不再需要知道文件的具体存放位置,所有的都由程序去管理,比如我要访问一张图片,只需要使用 “/image/view/1“即可,这就是ID为1的图片的发布网址,如果想直接获取图片的URL地址,则使用 “/image/1”
  • 我不准备使用编辑器,Markdown就很不错,直接把任何文本都以Markdown的格式编辑,如果以后这个系统发布的话,还提供让使用者自己添加格式化标记库的方法,只是现在我还只是自己用。
  • 一张图片可以属于多个图集,这是肯定的,比如我在北京拍了一张花的照片,那么这张照片当然可以属于花,也当然可以属于北京。
  • ……

大体上的功能就是这些吧,先把上面这些想法实现了再说,差不多需要一个星期的开发时间吧,然后就直接开始使用,在使用的过程中再去完善它……同时这个程序也可以为我之后的平台做基础……

今天突然就这么灵感闪现,想到好像还从来没有人开发一款3D俄罗斯广场游戏,我的意思不是说显示出来看着像3D,但是玩法和以前的一样的那种,而是真正的在三维空间里面进行的俄罗斯方块游戏,任何一个方块可以向左、右、前、后四个方向移动,同时还可以向左、右、上、下四个方向转动,当然了,不再是一行就消除,而是必须要把一个平面填满。

这个想法看上去是可行的,但是首先一个能想到的问题是,在以前的俄罗斯方块中,我们可以看到底层未填满的空格,但是在3D的环境下,这个不太好实现,我们只能看到最上面一层的状况,但是还是想开发这么一款游戏,操作其实很简单:

  • W : 在Z轴上向前移动(看上去就是越来越远离屏幕)
  • S : 在Z轴上向后移动 (与W键的效果相反)
  • A : 在X轴上向后移动 (即向屏幕左方移动)
  • D : 在X轴上向前移动(即向屏幕右方移动)
  • I : 向上旋转
  • K : 向下旋转
  • J : 向左旋转
  • L : 向右旋转
  • Space : 确定
然后还需要有一个办法让玩者可以把整个场景左右旋转,我选择下面这两个键:
  • R :场景向左旋转
  • Y : 场景向右旋转

这样正好两只手都在键盘上面时就可以完全控制整个游戏

我现在使用的系统版本是 OS X 10.7.4,以前一直都更新正常,但是最近的一次更新总是会提示无法安装任何更新,一直也没有找到好解决办法,从下面这个图可以看到,系统是有更新的,并且好像已经下载成功了,但是当我点击安装 4 项之后,就提示“未能安装选定的任何更新,出现未知错误”。

点击安装 4 项之后就出现下面这个提示:

哪位出现过同样问题的朋友给个建议啊,这个问题好像是出现在我安装了Final Cut Pro X 之后,有可能是因为我安装的 FCPX 不是正版的原因吧?我删除这个软件再试试。

— 更新 —

刚才把Final Cut Pro X删除了,然后连接了 VPN ,之后就可以更新了,但是我想原因应该还是出在 VPN 上面,可能是苹果的更新服务器的域名解析出问题了吧,最后有一个 JavaForOSX无法更新,我是自己去官方网站上下载的1.0手工安装更新的。

不知道是从什么时候开始喜欢喝饮料的,而且有很多时候不喝一点水之外的液体总是感觉嘴巴里不舒服,前几天无意间在一家店里看到这种进口饮料,不过我喝的不是这个味的,是芒果的,味道十分不错,但是第二次去买的时候就已经没有了,难到他们进货都只进一两瓶?应该不会吧。

CHABAA JUICE DRINK

CHABAA JUICE DRINK

整个过程十分的简单,部属方式也确实十分简单,它不像 wsgi 那么麻烦,整个体验十分的不错,这里记下来我的整个配置过程。

安装 UWSGI

默认系统并没有安装UWSGI,在使用前,必须得先安装它,我使用的是 Ubuntu 服务器,可以使用下面的方法安装它:

sudo -s
add-apt-repository ppa:nginx/stable
add-apt-repository ppa:uwsgi/release
apt-get update
apt-get install uwsgi-python python-virtualenv

你可以在运行上面的命令时, 会提示没有 “add-apt-repository” 的错误,这是因为这个工具并没有随Ubuntu一同安装,你需要自己安装它,安装方法是下面这行命令:

sudo apt-get install python-software-properties

从 Ubuntu 9.10 开始我们可以使用 add-apt-repository 命令(实质是个python脚本)在source.list里添加 ppa 源了(同时完成导入key)

创建Wiki站点的配置文件

我的维基架设在 http://wiki.costony.com 上,准备使用Nginx将其转发到 Unix Socket 上,我所使用的 Nginx 虚拟机的配置文件如下:

server {
    listen 80;
    access_log /home/wwwlogs/wiki.costony.com.access.log;
    error_log /home/wwwlogs/wiki.costony.com.error.log;

    server_name wiki.costony.com;
    location / {
        include uwsgi_params;
        uwsgi_pass unix:///tmp/wiki.costony.com.socket;
        uwsgi_param UWSGI_PYTHOME /home/websites/costony.com/wiki/moinenv/;
        uwsgi_param UWSGI_CHDIR /home/websites/costony.com/wiki/;
        uwsgi_param UWSGI_SCRIPT moin_wsgi;
        uwsgi_param UWSGI_SCHEME $scheme;
        uwsgi_param REMOTE_USER $remote_user;
    }

    location /moin_static194/ {
        alias /home/websites/costony.com/wiki/public/static/;
    }
}

从上面的配置中我们可以看到:

  • 我将Moin安装在它专用的 Virtuan Env中,同时,将其Static文件存放中一个特别的目录下,所以,这些都需要在安装 Moin的时候进行设定。

安装Python环境及 Moin

virtualenv /home/websites/costony.com/wiki/moinenv
cd /tmp
wget http://static.moinmo.in/files/moin-1.9.4.tar.gz
tar xvfz moin-1.9.4.tar.gz
source /home/websites/costony.com/wiki/moinenv/bin/activate
cd /tmp/moin-1.9.4
python setup.py install
deactivate
cp -r /tmp/moin-1.9.4/wiki /home/websites/costony.com/wiki
cd /home/websites/costony.com/wiki/wiki
mv wiki moin
cd moin
cp config/wikiconfig.py wikiconfig.py
cp server/moin.wsgi moin.wsgi
cp moin.wsgi moin_wsgi.py
cp /tmp/moin-1.9.4/MoinMoin/web/static/htdocs/* /home/websites/costony.com/wiki/public/static/
uwsgi-python -s /tmp/wiki.costony.com.socket --wsgi-file moin_wsgi.py -M -p 4 -d /home/wwwlogs/uwsgi.log

安装完成

在计算机图形学与电影摄影术中,高动态范围成像(英语:High Dynamic Range Imaging,简称HDRI或HDR)是用来实现比普通数字图像技术更大曝光动态范围(即更大的明暗差别)的一组技术。高动态范围成像的目的就是要正确地表示真实世界中从太阳光直射到最暗的阴影这样大的范围亮度。

我使用的HDR工具是Photoshop CS4(不用想,我用的是注册机算出来的注册号注册的版本)的自动HDR工具,使用起来十分的简单,而且效果也还不错,只是前期照片的拍摄是一大问题,还没有掌握如何来拍摄最适合的照片用以合成HDR照片,这是拍摄技术的问题了。

我所拍摄的地点是长沙市侯家塘,从二十二楼窗户上面拍的,拍这个和制作全景图时前期的拍摄一样,一个稳定的相机平台必不可少,我也一直没买三脚架,所以,就把相机放在窗台上面了,只是取景的角度不太好调整了,下面这张是合成之后的照片:

湖南省长沙市侯家塘HDR建筑 湖南省长沙市侯家塘HDR建筑

上面这张图片是由下面三张源始照片利用Photoshop的高动态范围成像技术合成的:

湖南省长沙市侯家塘HDR建筑源始图片湖南省长沙市侯家塘HDR建筑 湖南省长沙市侯家塘HDR建筑源始图片湖南省长沙市侯家塘HDR建筑

使用了正常曝光至过曝的三张照片合成的 使用了正常曝光至过曝的三张照片合成的

从上面三张照片可以看到,任何一张照片都只有部分区域曝光正常,最左侧的照片天空部分过曝了,而越往后侧,照片下部建筑区域的暗部就越来越暗以至于已经没有了任何细节,通过合成之后,我们让高光与暗部都尽可能的保留细节。

— 2012.07.15 更新 —

今天在Apple App Store购买了一个名为 HDR Darkroom Pro 的HDR合成软件,下面我使用了六张照片再一次合成了上面这张照片,效果如下图所示:

使用了正常曝光至过曝的三张照片合成的

使用了所有六张照片(从曝光完全不足至过曝的六张) 使用了所有六张照片(从曝光完全不足至过曝的六张)

默认情况下还是Photoshop的效果我最喜欢,但是应该照片在合成的过程中是可以调整的,所以,就看个人的调整技术了,我才刚刚开始接触HDR这个东西(虽然几年前玩过但是都没有去研究过),再加上本人对色彩的控制能力太差,唉,有待提高啊……

不过HDR Darkroom PRO使用起来比Photoshop方便快捷,而且资源占用少得多,调整也方便,所以,这个可以是个首选啊……另外,有一点点感觉上的收获,如果要合成HDR照片,所使用的照片数量不需要太多,也不是很少就可以的,最简单的办法来确定哪几张照片适合合成HDR图,看看它们的直方图就行了,看看他们的高峰是不是从左至右都有,如果有的话合成的一般都还不错……

突然想起来上面两张图片的问题其实很大,我使用的ProPhoto RGB色彩空间,这样照片经过PHP的GD库处理后,就成上面这个严重偏色的了,可以点击图片查看正常的效果……其实还是不错的。

HDR 窗台上的富贵竹 HDR 窗台上的富贵竹

学习HDR最好的素材就是窗台,因为窗内与窗外的光强返差总是很大,我们可以通过不同的曝光让窗外的景物正常曝光或者让窝内的曝光,但是我们总是无法让窗内外曝光都正常,这个时候使用HDR就很容易做到了,如上图所示就是我拍了三张照片合成的HDR,只是自己技术不到位,总是感觉这真太不真实了……

在 Smashing Magazine 上面有一篇《35 Fantastic HDR Pictures》的文章,里面分享了35张十分不错的HDR作品。

选择公司的形式

普通的有限责任公司,最低注册资金3万元,需要2个(或以上)股东,从06年1月起新的公司法规定,允许1个股东注册有限责任公司,这种特殊的有限责任公司又称“一人有限公司”(但公司名称中不会有“一人”字样,执照上会注明“自然人独资”),最低注册资金10万元。

 

如果你和朋友、家人合伙投资创业,可选择普通的有限公司,最低注册资金3万元;如果只有你一个人作为股东,则选择一人有限公司,最低注册资金10万元。

注册公司的步骤

  1. 核名:到工商局去领取一张“企业(字号)名称预先核准申请表”,填写你准备取的公司名称,由工商局上网(工商局内部网)检索是否有重名,如果没有重名,就可以使用这个名称,就会核发一张“企业(字号)名称预先核准通知书”。这一步的手续费是30元。 (30元可以帮你检索5个名字,很多名字重复,所以一般常见的名字就不用试了,免得花冤枉钱)
  2. 租房:去专门的写字楼租一间办公室,如果你自己有厂房或者办公室也可以,有的地方不允许在居民楼里办公。租房后要签订租房合同,并让房东提供房产证的复印件。
  3. 买印花税:签订好租房合同后,还要到税务局去买印花税,按年租金的千分之一的税率购买,例如你的每年房租是1万元,那就要买10元钱的印花税,贴在房租合同的首页,后面凡是需要用到房租合同的地方,都需要是贴了印花税的合同复印件。
  4. 编写“公司章程”:可以在工商局网站下载“公司章程”的样本,修改一下就可以了。章程的最后由所有股东签名。
  5. 刻私章:去街上刻章的地方刻一个私章,给他们讲刻法人私章(方形的)。费用大概20元左右。
  6. 到会计师事务所领取“银行询征函”:联系一家会计师事务所,领取一张“银行询征函”(必须是原件,会计师事务所盖鲜章)。如果你不清楚,可以看报纸上的分类广告,有很多会计师事务所的广告。
  7. 去银行开立公司验资户:所有股东带上自己入股的那一部分钱到银行,带上公司章程、工商局发的核名通知、法人代表的私章、身份证、用于验资的钱、空白询征函表格,到银行去开立公司帐户,你要告诉银行是开验资户。开立好公司帐户后,各个股东按自己出资额向公司帐户中存入相应的钱。银行会发给每个股东缴款单、并在询征函上盖银行的章。 注 意:公司法规定,注册公司时,投资人(股东)必须缴纳足额的资本,可以以贷币形式(也就是人民币)出资,也可以以实物(如汽车)、房产、知识产权等出资。 到银行办的只是货币出资这一部分,如果你有实物、房产等作为出资的,需要到会计师事务所鉴定其价值后再以其实际价值出资,比较麻烦,因此建议你直接拿钱来 出资,公司法不管你用什么手段拿的钱,自己的也好、借的也好,只要如数缴足出资款即可。
  8. 办理验资报告:拿着银行出具的股东缴款单、银行盖章后的询征函,以及公司章程、核名通知、房租合同、房产证复印件,到会计师事务所办理验资报告。一般费用500元左右(50万以下注册资金)。
  9. 注册公司:到工商局领取公司设立登记的各种表格,包括设立登记申请表、股东(发起人)名单、董事经理监理情况、法人代表登记表、指定代表或委托代理人登记表。填好后,连同核名通知、公司章程、房租合同、房产证复印件、验资报告一起交给工商局。大概3个工作日后可领取执照。此项费用约300元左右。
  10. 凭营业执照,到公安局指定的刻章社,去刻公章、财务章。后面步骤中,均需要用到公章或财务章。
  11. 办理企业组织机构代码证:凭营业执照到技术监督局办理组织机构代码证,费用是80元。办这个证需要半个月,技术监督局会首先发一个预先受理代码证明文件,凭这个文件就可以办理后面的税务登记证、银行基本户开户手续了。
  12. 去银行开基本户:凭营业执照、组织机构代码证,去银行开立基本帐号。最好是在原来办理验资时的那个银行的同一网点去办理,否则,会多收100元的验资帐户费用。开基本户需要填很多表,你最好把能带齐的东西全部带上,要不然要跑很多趟,包括营业执照正本原件、身份证、组织机构代码证、公财章、法人章。开基本户时,还需要购买一个密码器(从2005年下半年起,大多银行都有这个规定),密码器需要280元。今后你的公司开支票、划款时,都需要使用密码器来生成密码。
  13. 办理税务登记:领取执照后,30日内到当地税务局申请领取税务登记证。一般的公司都需要办理2种税务登记证,即国税和地税。费用是各40元,共80元。办理税务登记证时,必须有一个会计,因为税务局要求提交的资料其中有一项是会计资格证和身份证。你可先请一个兼职会计,小公司刚开始请的兼职会计一般200元工资就可以了。
  14. 申请领购发票:如果你的公司是销售商品的,应该到国税去申请发票,如果是服务性质的公司,则到地税申领发票。

最后就开始营业了。 注意每个月按时向税务申报税哦,即使没有开展业务不需要缴税,也要进行零申报,否则会被罚款的。

有二点你可能比较关心:

  1. 公司必须建立健全的会计制度:你可能担心自己不会,怎么办?刚开始成立的公司,业务少,对会计的工作量也非常小,你可以请一个兼职会计,每个月到你的公司帮你建帐,二、三天时间就够了,给他200-500左右的工资即可。
  2. 公司的税额:营业税:销售商品的公司,按所开发票额的4%征收增殖税;提供服务的公司,按所开发票额的5%征收营业税。所得税:对企业的纯利润征收18-33%的企业所得税。 小公司的利润不多,一般是18%。对企业所得税,做帐很关键,如果帐面上你的利润很多,那税率就高。所以,平常的购买设备都要开发票,你吃饭、坐车的票都留起来,可以做为你的企业运作成本。

二种税的区别:营业税是对营业额征税,不管你赚没有赚钱,只有发生了交易,开了发票,就要征税;所得税,是对利润征税,利润就是营业额扣减各种成本后剩余的钱,只有赚了钱,才会征所得税。

还有其它各种各样很多种的税,但没有多少钱,主要是上面二种,特别是所得税非常高 .

#!/usr/bin/env python
# encoding: utf-8
"""
File: prime_list.py
Created by Pan Tao on 2011-11-16.
Copyright (c) 2011 CosTony.Com. All rights reserved.
"""
prime_list = lambda n: set([i for i in range(2,n)]) ^ set([i for i in range(2,n) for x in range(2,int(i**0.5)+1) if i%x == 0])
def main():
    print(prime_list(int(raw_input('Enter the max number:'))))
if __name__ == '__main__':
    main()

不要看上面有那么多行字符哈,其实就 prime_list = lambda: .... 这一行就行了,如果你对质数感兴趣,也可以去看看我以前写的一个专门用来求质数的程序,见这里: Python 验证与获得素数(via Miller Rabin & AKS)

在了解验证与获得素数前,先来了解一下什么是素数:

质数,又称素数,指在一个大于1的自然数中,除了1和此整数自身外,无法被其他自然数整除的数(也可定义为只有1和本身两个因数的数)。比1大但不是素数的数称为合数。1和0既非素数也非合数。素数在数论中有着很重要的地位。最小的素数是2,也是素数中唯一的偶数(双数);其他素数都是奇数(单数)。质数有无限多个,所以不存在最大的质数。围绕著素数存在很多问题、猜想和定理。著名的有孪生素数猜想和哥德巴赫猜想。素数序列的开头是这样的:2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113

根据素数的定义想到的一些解决办法

从上面我们可以知道,任何一个大于 1 且除1和自身之外不能被其它整数整除的整数为素数,那么我们首先可能就会想到一个办法来验证一个整数是不是素数:给定一个整数 n ,让 n 依次除以 1 -> n 之间的所有整数,如果都没有能整除的,那么这个数肯定是素数:

def factor(n):
'''Get number's factors'''
end = n+1
for i in xrange(1,end):
if n % i == 0 : yield i
def force_checker(n):
'''Checker weither a number is a prime number'''
if len([i for i in factor(n)]) == 2: return True
return False

上面的代码我们作了什么?其实很简单,根据前面我们的分析,对于任何一个需要验证的数字,我们首先需要得到它的因子,这个工作是由 factor() 函数来完成的,遍历 1 -> n 之间的所有整数,一个一个测试,如果是这个数的因子,则将其返回( yield i )这一句,所以 factor() 是一个“生成器”:/code/python-functions-decorators ,有了这个之后,我再写了一个 force_checker() 函数,来暴力验证,办法还是最原始的,先找到整数 n 的所有因数子,然后检查其个数,如果为两个(也就是 1 与其自身),则这个数是素数。

上面的这两个函数确实是已经可以实现素数的验证了,但是对于一整数,还没有任何关系,但是如果整数过大,那就麻烦了,你的电脑将有很长的时间,CPU 处于 100%的状态,我们可以来作一个测试,来查看一下验证需要多少时间:

>>> from time import time
>>> def test(n):
... print(time())
... print force_checker(n)
... print(time())
...
>>> test(104729)
1317222986.09
True
1317222986.1
>>> test(1047290)
1317222997.53
False
1317222997.66
>>> test(10472900)
1317223000.42
False
1317223001.55

从上面我们可以看到,对于6位的这个数字,整个验证花了不到0.01 秒,我在 104729 后面追加了一个 0 之后,花了 0.13 秒,但是当我再追加一个 0 之后,就花了 1.13 秒了,经过我自己电脑上面的验证,对于大于 10 位的数字,这种办法根本就行不通,所以,还得想其它办法。

我再想到是,先对数字进行过滤,我想到的是下面这些数字其实根本就不需要去测的:

  1. 未位为(0,2,4,5,6,8)的数字都能被 2 整除,所以肯定不是素数
  2. 每一位相加得到的数字如果能被 3 整除,那么这个数也能被 3 整除
  3. 任何一个大于一个整数 ½ 的整数,绝对不是该整数的因子

跟着上面的思路,我想到了先得修改一下下 force_checker() 函数:

def force_checker(n):
'''Checker weither a number is a prime number'''
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
if len([i for i in factor(n)]) == 2: return True
return False

来看看现在的效果:

>>> test(10472900)1317223764.19
False
1317223764.19
>>> test(104729001)
1317223822.58
False
1317223822.58

几乎是不花时间就能检测出我所提供的这两个数字是否为素数,但是你可能也看到了,我其实是取了一个特别的数字,一个是 0 结尾的,一个是各位数字之和为 3 的整数倍的,但是我们还是可以通过另一个办法来看看加了上面的一行判断之后的实际效果,我们来生成一个素数列,看看需要花多少时间:

我们先来写一个用来生成素数数列的函数: prime_generator() ,它是一个生成器,同时,为了以后的测试方法,我还写了一个检测运行时间的函数 test() ,现在我的所有函数是下面这样的:

import sys
def factor(n):
'''Get number's factors'''
end = n+1
for i in xrange(1,end):
if n % i == 0 : yield i
def force_checker_old(n):
if len([i for i in factor(n)]) == 2: return True
return False
def force_checker(n):
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
if len([i for i in factor(n)]) == 2: return True
return False
def prime_generator(limit = None, start = 1, end = None, checker = force_checker):
seek, counter = start, 0
while True:
if limit is not None and counter >= limit: break
if end is not None and seek >= end: break
if checker(seek):
yield seek
seek += 1
counter += 1
else:
seek += 1
def test(limit = None, start = 1, end = None, checker = force_checker):
import sys
from time import time
print('Generate prime numbers with {} checker.'.format(checker.__name__))
time_start = time()
column, counter = 1, 1
for prime in prime_generator(limit,start,end,checker):
sys.stdout.write('{:>5} : {:<10}'.format(counter, prime))
counter += 1
if column % 5 == 0:
sys.stdout.write('n')
column += 1
time_end = time()
print('nnI got {} results in {} seconds(form {} to {}).'.format(
counter-1,time_end - time_start, time_start, time_end))
def main():
test(1000)
test(1000,checker=force_checker_old)
if __name__ == '__main__':
main()

上面代码中 prime_generator() 就是一个素数生成器,我们可以在 for 循环中由 limit 限制的数量个素数,如果 limit 设置为 None,则将不限制素数个数(在 for 循环中,不建议这么干), start 表示我们从哪一个数开始生成,默认为 1 ,表示需要生成 大于 1 的素数, end 默认为 None ,表示不限制最大素数(不是最大素数个数),由于我们需要测试不通的素数测试方法的性能,所以我这里使用了一个 checker 参数,默认使用 force_checker() ,但是我们可以指定不同的验证函数,只要该函数是同样的使用方法(传入一个数,返回 True 或者 False)。

test() 函数是我写的一个测试函数,我们将由它来调用 prime_generator() 函数,并且记录整个程序的运行时间与素数个数,我还将上面的所有代码都保存在一个名为 prime.py 的文件中,这样我可以直接使用我的 TextMate 编辑器运行它,或者你也可以使用 :

$python /path/to/prime.py

命令来运行,我们来测试一下吧,在我的机器上面,同时用两个验证器来验证素数,生成 1000 条记录的时间如下:

Generate prime numbers with force_checker checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
996 : 7879 997 : 7883 998 : 7901 999 : 7907 1000 : 7919
I got 1000 results in 1.05513501167 seconds(form 1317225770.58 to 1317225771.64).
Generate prime numbers with force_checker_old checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
996 : 7879 997 : 7883 998 : 7901 999 : 7907 1000 : 7919
I got 1000 results in 3.41558718681 seconds(form 1317225771.64 to 1317225775.05).

上面的结果一目了然了吧,整整快了2.4秒(机器不同,运行时间与环境不同,可能时间有些许不一样,你的结果也可能和我的不一样,但是绝对是快,而且快很多),在我这里测试获取 2000 个素数的结果是:优化之后的函数使用 4.46425414085 秒,未优化的使用了 15.689232111 秒,获取个数不断增加,它们之间的时间差将成倍的增加。当然,上面优化过后的函数也不会是最好的,为什么我给这个函数取名叫作 force_checker() ?那是因为它是最笨的办法,暴力验证,就是不管你是什么数,我一个一个试,只到试完为止,这是一种没有任何技巧可言的方法。

Miller Rabin 验证法

上面的方法对于小整数而言,速度已经很快了,但是在我们现在的现实应用中,基本上不可能使用上在的这个办法,比如目前素数应用最广的应用领域公共密钥体系中,一般选择的素数都相当的大,至少都在100位以上,而上面的这个办法,对于10位的数字就已经力不从心了,而对于100位的数字,可能需要耗尽你一生的时间才能验证成功,所以,还得选择其它的方法,比如最有名的一个验证素数的办法:Rabin Miller,按我自己的想法,这个属性概率验证法吧,如果证明这个方法的有效性不是我所研究的对象,我也没这个能力,但是至少它是被所有人都认同的快速的验证法。

Miller Rabin 验证法的细节是这样的:

  1. 选择一个代测的随机数 p ,计算 b , b 是 2 整除 p - 1 的次数,然后计算 m ,使用 n = 1 + (2**m) 。
  2. 选择一个小于 p 的随机数 a 。
  3. 设 j = 0 且 z = (a**m) mod p 。 # 如果 z = 1@ 或者 z = p - 1 ,那么 p 通过测试,可能是素数
  4. 如果 j > 0 且 z = 1 ,那么 p 不是素数
  5. 设 j = j + 1 如果 j < b 且 z != p - 1 , 设 z = (z**m) mod p ,然后咽到上面步骤,如果 z = p - 1 ,那么 p 通过测试,可能为素数
  6. 如果 j = b 且 z != p - 1 ,不是素数

数 a 被当成证据的概率为 75% ,这意味着我们在上面的测试过程中,迭代次数为 t 时,产生一个假素数所花费的时间不会超过 1/4**t ,当我们迭代 5 次时,产生素数的概率已经高达 99.999% 了, 这几乎可以说是一个素数了,而国际上通用的作用是,必须迭代50次,下面我们来在我们的prime.py 中实现它:

def miller_rabin_checker(n):
'''Checker wiether a number is a prime number via rabin miller method'''
if n <= 1: return False
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
import sys, random
def to_binary(n):
r = []
while n > 0:
r.append(n % 2)
n = n / 2
return r
def test(a, n):
'''test(a, n) -> bool Test whether n is complex
Returns:
- True : if n is complex
- False: if n is probably prime
'''
b = to_binary(n-1)
d = 1
for i in xrange(len(b) - 1, -1, -1):
x = d
d = (d * d) % n
if d == 1 and x != 1 and x != n - 1:
return True
if b[i] == 1:
d = (d * a) % n
if d != 1:
return True
return False
def checker(n, s = 50):
'''checker(n, s = 50) -> bool checks whether n is prime or not
Returns:
- True : if n is probably prime.
- False: if n is complex.
'''
for j in xrange(1, s + 1):
a = random.randint(1, n - 1)
if test(a,n):
return False
return True
return checker(n)

我们同样可以使用 test() 函数来验证上面的这个函数的效率:

Generate prime numbers with miller_rabin_checker checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
96 : 503 97 : 509 98 : 521 99 : 523 100 : 541
I got 100 results in 0.0682229995728 seconds(form 1317228097.28 to 1317228097.35).
Generate prime numbers with force_checker checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
96 : 503 97 : 509 98 : 521 99 : 523 100 : 541
I got 100 results in 0.0126042366028 seconds(form 1317228097.35 to 1317228097.38).
Generate prime numbers with force_checker_old checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
96 : 503 97 : 509 98 : 521 99 : 523 100 : 541
I got 100 results in 0.0203368663788 seconds(form 1317228097.38 to 1317228097.39).

上面的数据显示运行时间由短到长的验证方法分别为:

force_checker < force_checker_old < miller_rabin_checker

这是怎么回事儿?其实很容易解释,对于小整数,force_checker_old 所作的计算量更少而已,但是如果对于大整数,由于 force_checker_old 或者 force_checker 所用的方法都是试除法,就是所有小于 n 的数字都拿去除一次,所以,待测整数越大,其时间就越多,而且时间是成倍的增加,来看下面的验证(下面的测试我将不再验证 force_checker_old() 函数):

生成1000个素数

I got 1000 results in 0.895807027817 seconds(form 1317228892.57 to 1317228893.47).
I got 1000 results in 3.35119318962 seconds(form 1317228898.47 to 1317228901.82).

生成10000个素数

Generate prime numbers with miller_rabin_checker checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
9996 : 104707 9997 : 104711 9998 : 104717 9999 : 104723 10000 : 104729
I got 10000 results in 11.3178238869 seconds(form 1317228989.72 to 1317229001.03).
Generate prime numbers with force_checker_old checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
9996 : 104707 9997 : 104711 9998 : 104717 9999 : 104723 10000 : 104729
I got 10000 results in 571.780455112 seconds(form 1317229006.04 to 1317229577.82).

这个差距就有点儿太大了哈,不过,这就是一个事实。

完整的程序文件: prime.py

#!/usr/bin/env python
# encoding: utf-8
"""
File: prime.py
Created by Pan Tao on 2011-09-28.
Copyright (c) 2011 CosTony.Com. All rights reserved.
"""
import sys
def factor(n):
'''Get number's factors'''
end = n+1
for i in xrange(1,end):
if n % i == 0 : yield i
def force_checker_old(n):
'''Checker weither a number is a prime number'''
if len([i for i in factor(n)]) == 2: return True
return False
def force_checker(n):
'''Checker weither a number is a prime number'''
if n <= 1: return False
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
if len([i for i in factor(n)]) == 2: return True
return False
def miller_rabin_checker(n):
'''Checker wiether a number is a prime number via rabin miller method'''
if n <= 1: return False
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
import sys, random
def to_binary(n):
r = []
while n > 0:
r.append(n % 2)
n = n / 2
return r
def test(a, n):
'''test(a, n) -> bool Test whether n is complex
Returns:
- True : if n is complex
- False: if n is probably prime
'''
b = to_binary(n-1)
d = 1
for i in xrange(len(b) - 1, -1, -1):
x = d
d = (d * d) % n
if d == 1 and x != 1 and x != n - 1:
return True
if b[i] == 1:
d = (d * a) % n
if d != 1:
return True
return False
def checker(n, s = 50):
'''checker(n, s = 50) -> bool checks whether n is prime or not
Returns:
- True : if n is probably prime.
- False: if n is complex.
'''
for j in xrange(1, s + 1):
a = random.randint(1, n - 1)
if test(a,n):
return False
return True
return checker(n)
def prime_generator(limit = None, start = 2, end = None, checker = force_checker):
'''Generator of prime numbers
varibles:
- limit : how many prime numbers
- start : where to start
- end : when to stop
- checker : the prime number checker, default to force_checker
'''
seek, counter = start, 0
while True:
if limit is not None and counter >= limit: break
if end is not None and seek >= end: break
if checker(seek):
yield seek
seek += 1
counter += 1
else:
seek += 1
def test(limit = None, start = 2, end = None, checker = force_checker):
import sys
from time import time
print('Generate prime numbers with {} checker.n'.format(checker.__name__))
time_start = time()
column, counter = 1, 1
for prime in prime_generator(limit,start,end,checker):
sys.stdout.write('{:>5} : {:<10}'.format(counter, prime))
counter += 1
if column % 5 == 0:
sys.stdout.write('n')
column += 1
time_end = time()
print('nnI got {} results in {} seconds(form {} to {}).'.format(
counter-1,time_end - time_start, time_start, time_end))
def main():
import sys
checkers = {'fo': force_checker_old, 'f': force_checker, 'mr': miller_rabin_checker, 'e': ''}
while True:
checker_help = '''
Which checker you want to use?
type 'fo' to choose force_checker_old checker
type 'f' to choose force_checker checker
type 'mr' to chose miller_rabin_checker
type 'e' to exit
'''
checker = raw_input(checker_help)
if checker not in checkers:
print('You can only type "fo", "f" or "rm" to choose checker, type again :n')
continue
if checker == 'e':
exit()
action_help = '''
What are you want to do?
type 'g' to generate prime numbers
type 'c' to check whether a number is a primen
'''
action = raw_input(action_help)
if action == 'g':
limit = raw_input('How many numbers do you want to generate?n')
start = raw_input('The min-number of the prime list is:n')
end = raw_input('The max number of the prime list is :n')
if int(end) <= int(start):
print('The end must bigger than start')
continue
if int(limit) < 1:
print('limit must bigger or equal to 1')
continue
test(int(limit), int(start), int(end), checkers[checker])
elif action == 'c':
if checkers[checker](int(raw_input('Enter the number you want to check:n'))):
print('This number is prime number')
else:
print('This number is not a prime number')
if __name__ == '__main__':
main()

setuid、 setgid 和 sticky 权限这几个权限以前从来没有了解过,甚至都不知道有这么三种权限在这里,安装 FreeBSD 的时候,看到了,找了些资料,了解了一下下,这个东西不了解不知道,一了解,解了我很多迷题。

先回忆一下下Linux的权限,在Linux里面或者其它的类Unix系统中,权限是管理得很严格的,任何一个用户做任何事情都必须的相关的权限才行,别人的东西不让你看,你根本就会不知道有这么个东西,那么好了,现在就有了下面这几个问题:

  1. 普通用户修改自己的权限,要修改系统的密码数据库,那密码数据库是不是对所有用户开放?如果是的话,那密码数据还安全?如果不是的话,那为什么所有用户又都可以去修改?
  2. 系统的临时目录 /tmp 里面的所有东西,是规谁的?因为任何一个用户都对这个文件目录有写的权限,那也就是说任何用户都可以修改这个目录里面的内容了,那怎么办?我的临时文件别人也可以删除?

先想想我以前所了解的知识,这种问题还真没法儿解决,但是操作系统却已经解决得很好了,为什么?原因就是今天发现的这三种权限类型。这些权限对于一些 UNIX 操作而言很重要, 因为它们能提供一些一般情况下不会授予普通用户的功能。 为了便于理解, 我们首先介绍真实用户 ID (real user ID) 和生效用户 ID (effective user ID)。

真实用户 ID 是拥有或启动进程的用户 UID。 生效 UID 是进程以其身份运行的用户 ID。 举例来说, passwd(1) 工具通常是以发起修改密码的用户身份启动, 也就是说其进程的真实用户 ID 是那个用户的 ID; 但是, 由于需要修改密码数据库, 它会以 root 用户作为生效用户 ID 的身份运行。 这样, 普通的非特权用户就可以修改口令, 而不是看到 “Permission Denied” 错误了。

setuid 权限可以通过在普通权限前面加上一个数字四 (4) 来设置, 如下面的例子所示:

# chmod 4755 suidexample.sh

这样一来, suidexample.sh 的权限应该如下面这样:

-rwsr-xr-x   1 trhodes  trhodes    63 Aug 29 06:36 suidexample.sh

您会注意到, 在原先的属主执行权限的位置变成了 s。 这样, 需要提升特权的可执行文件, 例如 passwd 就可以正常运行了。

可以打开两个终端来观察这一情形。 在其中一个终端里面, 以普通用户身份启动 passwd 进程。 在它等待输入新口令时, 在另一个终端中查看进程表中关于 passwd 命令的信息。

在终端 A 中:

Changing local password for trhodes
Old Password:

在终端 B 中:

# ps aux | grep passwd
trhodes  5232  0.0  0.2  3420  1608   0  R+    2:10AM   0:00.00 grep passwd
root     5211  0.0  0.2  3620  1724   2  I+    2:09AM   0:00.01 passwd

正如前面所说的那样, passwd 是以普通用户的身份启动的, 但其生效 UID 是 root。

与此对应, setgid 权限的作用, 与 setuid 权限类似, 只是当应用程序配合这一设定运行时, 它会被授予拥有文件的那个组的权限。

如果需要在文件上配置 setgid 权限, 可以在权限数值前面增加数字二 (2) 来运行 chmod 命令, 如下面的例子所示:

# chmod 2755 sgidexample.sh

可以用与前面类似的方法来检视新设定的生效情况, 在组权限的地方的 s 表示这一配置已经生效:

-rwxr-sr-x   1 trhodes  trhodes    44 Aug 31 01:49 sgidexample.sh
注意: 在这些例子中, 尽管 shell 脚本也属于可执行文件的一种, 但它们不会以您配置的 EUID 或生效用户 ID 的身份运行。 这是因为 shell 脚本可能无法直接呼叫 setuid(2) 调用。

我们已经讨论了两个特殊权限位 (setuid 和 setgid 权限位), 它们让用户在使用程序时能够用到更高的权限, 有时这会削弱系统的安全性。 除了这两个之外, 还有第三个特殊权限位: sticky bit, 它能够增强安全性。

当在目录上设置了 sticky bit 之后, 其下的文件就只能由文件的所有者删除了。 这个权限设置能够防止用户删除类似 /tmp 这样的公共目录中不属于他们的文件。 要应用这种权限, 可以在权限设置前面加上数字一 (1)。 例如:

# chmod 1777 /tmp

现在, 可以用 ls 命令来查看效果:

# ls -al / | grep tmp
drwxrwxrwt  10 root  wheel         512 Aug 31 01:49 tmp

这里的结尾的 t 表示了 sticky bit 权限。

这是一个简单的基于 ZODB3 的数据存储,定义了一个用户User类,一个应用 App 类,App类用来操作User数据,连接默认使用的 ZEO。你需要至少安装Python 2.3 及以上版本并且安装了 ZODB 3

#!/usr/bin/env python
# encoding: utf-8
"""
user.py
Created by pantao on 2012-04-17.
Copyright (c) 2012 aitine.com. All rights reserved.
"""
import sys, os
import transaction
from persistent import Persistent
from ZEO import ClientStorage
import ZODB
from ZODB.POSException import ConflictError
from BTrees import OOBTree

class User(Persistent):
    """Class for a user
    """
    def __init__(self, login, email, password):
        self.login = login.lower()
        self.email = email.lower()
        self.password = password

class App():
    """"""
    def __init__(self, conn):
        self.conn = conn
        self.root = self.conn.root()

    def get_users(self):
        if not self.root.has_key('users'):
            return None
        return self.root['users'].values()

    def add_user(self, user):
        if not self.root.has_key('users'):
            print('Create new ')
            self.root['users'] = OOBTree.OOBTree()
            transaction.commit()
        users = self.root['users']
        if not users.has_key(user.login):
            users[user.login] = user
            transaction.commit()
        else:
            return None
        return users[user.login]

def main():
    app = App(ZODB.DB(ClientStorage.ClientStorage('/tmp/zeosocket')).open())
    while 1:
        us = app.get_users()
        if us:
            for u in us:
                print('<User {} : {} >'.format(u.login, u.email))
        l,e,p = raw_input('Enter user info:').split(' ')
        u = User(l,e,p)
        app.add_user(u)

if __name__ == '__main__':
    main()

Markdown 介绍

在教程正式开始之前,请先打开 Markdown 测试工具 ,这里面的所有示例都可以直接在该测试工具中测试,或者你也可以将 Markdown 的脚本下载至自己的电脑测试:

Perl 实现 : Markdown 1.0.1

PHP 实现 : PHP Markdown 1.0.1 : PHP Markdown Extra 1.2.5

Python 实现 : Python Markdown 2.1.1 : 要安装最新版本的 Markdown,在Python中还可以使用下列方法安装

sudo easy_install markdown
     sudo pip install markdown

Markdown 是这样两种东西,首先,他是一个格式化语法结构,其次他是一个使用Perl语言编写的用来实现Markdown至HTML转换的工具,它的设计目标就是让写作者尽可能使用容易阅读的语法书写,而不必被繁琐的HTML标签结构所纠缠,就像下面这样就可以创建一个一级标题的HTML结构:

# Markdown 介绍 #

Markdown 基本语法详解

使用 Markdown 一般情况下是没有可视化编辑界面的(Markdown与HTML的不同点在于它的设计初忠就是要写作者使用转Markdown代码(这里称之为代码吧,其实不是,它就是纯文本)),有一些网站在使用的时候,写作界面分为两个区域,预览区域和编辑区域,编辑区域就是写 Markdown,预览区会动态的将编辑区的修改即时的转换为HTML后展示出来以达到可视化编辑 的效果,但是我们这里所讲的,仅仅只是Markdown的语法。

段落、标题以及块引用

要使用 Markdown创建段落应该是最简单的了,只需要某一段文字顶头书写然后前后空行即可,这里不再做过多说明。

标题的创建也十分简单,这里有两种方法,最常用的就是在要作为标题的文字下一行(不空行)添加超一个的= 号或者 - 号,例如:

这是一个一级标题
==============

这是一个二级标题
---------------------------

将被转换成为:

<h1>这是一个一级标题</h1>

<h2>这是一个二级标题</h2>

另外一种创建标题的方法能创建出所有六级标题,它们分别是在要创建标题的那一行方案最前面顶行创建一个或者多个 # 号,并以相同数量的 # 结尾,比如:

### 这是三级标题 ###

##### 这是五级标题

将被转换成为:

<h3>这是三级标题</h3>

<h5>这是五级标题</h5>

你可以看到,后面结尾的 # 是可以省略的。

块级引用的创建是在需要作为块引用的所有文本行前面加上 > 符号,比如:

> 这是块引用的第一行,将会成为块引用中的一个段落
>
> 这是块引用的第二段
> ## 这是块引用中的一个二级标题

转被转换成为下面这样的HTML代码:

<blockquote>
<p>这是块引用的第一行,将会成为块引用中的一个段落</p>

<p>这是块引用的第二段</p>

<h2>这是块引用中的一个二级标题</h2>
</blockquote>

加强与强调

Markdown 使用 星号(*) 与 下划线(_) 表示加强与强调,如下所示:

胡潇说:”我 *不想* 和鸟蛋睡一起!“
刘钊说:“我 _更不想_ 和潇老娘子睡一起!”

然后潘韬就说了:“你们两个都 **不要睡了** 吧!”
潘韬还说了:“ __都跟__ 哥睡!”

将被转换成为:

<p>胡潇说:”我 <em>不想</em> 和鸟蛋睡一起!“
刘钊说:“我 <em>更不想</em> 和潇老娘子睡一起!”</p>

<p>然后潘韬就说了:“你们两个都 <strong>不要睡了</strong> 吧!”
潘韬还说了:“ <strong>都跟</strong> 哥睡!”</p>

列表

无序列表可以使用星号,加号或者减号(*、+、-)创建,它们三者效果都是一样的,比如下的示例:

* 第一项
* 第二项
* 第三项

+ 第一项
+ 第二项
+ 第三项

还有

- 第一项
- 第二项
- 第三项

都会被转换成为:

<ul>
<li>第一项</li>
<li>第二项</li>
<li>第三项</li>
</li>

有序列表则直接在每一项前面加上阿拉伯数字与小数点即可,如下:

1. 有序列表第一项
2. 有序列表第二项
3. 有序列表第三项

将被转换成为:

<ol>
<li>有序列表第一项</li>
<li>有序列表第二项</li>
<li>有序列表第三项</li>
</ol>

如果您在每一个列表项之间添加了一个空行,那么主会在每一个列表项中创建一个段落,比如:

* 这是一个列表项

  这是第一列表项中的第二个段落

* 这是列表的另一个项目

将被转换成为:

<ul>
<li><p>这是一个列表项</p>
<p>这是第一列表项中的第二个段落</p></li>
<li><p>这是列表的另一个项目</p></li>
</ul>

链接

Markdown使用两种方法创建链接,行内创建与引用创建,行内创建一般使用得比较多,它的创建语法是下面这样的:

这是一个示例[链接](http://aitine.com)。

转换成为:

<p>这是一个示例<a href="http://aitine.com">链接</a>。</p>

如果要为链接指定 title 属性,刚可以使用下面这样的:

这是一个示例[链接](http://aitine.com "这是一个标题")。

而以引用的方式创建链接一般用在学术论文上面比较多,或者另一种情况,如果某一个链接在文章中多处使用,那么使用引用 的方式创建链接将非常好,它可以让你对链接进行统一的管理,这有点类似于 LaTeX 对文档引用的管理,创建方法如下:

我最喜欢的几个网站是[Google][1] 、[艾天科技][2] 以及 [自己的博客][3],但是最喜欢的却是 [艾天项目管理平台][atoa],因为 [谷歌][1] 老是不能被访问到。

[1]:http://www.google.com "Google"
[2]:http://aitine.com "Aitine Technology"
[3]:http://note.costony.com "Notes of Cos Tony"
[atoa]:http://us.aitine.com "艾天项目管理平台"

转换后得到:

<p>我最喜欢的几个网站是<a href="http://www.google.com" title="Google">Google</a> 、<a href="http://aitine.com" title="Aitine Technology">艾天科技</a> 以及 <a href="http://note.costony.com" title="Notes of Cos Tony">自己的博客</a>,但是最喜欢的却是 <a href="http://us.aitine.com" title="艾天项目管理平台">艾天项目管理平台</a>,因为 <a href="http://www.google.com" title="Google">谷歌</a> 老是不能被访问到。</p>

从Markdown链接的引用创建方法中,你应该了解到一些基本的引用管理知识,要知道,专业的印刷行业里面、学术论文写作里面一般都不是使用 Word 或者什么可视化编辑工具来书写的,因为这些工具的引用管理功能都太弱了,而且使用起来十分的麻烦,一般都是使用 Tex 或者 LaTex ,有兴趣的可以去了解一下。

图片

图片的语法与链接的十分相似,行内创建时:

![Alt 文本](/path/to/img.jpg "图片 Title")

转换成为:

<p><img src="/path/to/img.jpg" alt="Alt 文本" title="图片 Title" /></p>

引用式创建也很相似:

![Alt 文本][id]

[id]:/path/to/img.jpg "标题"

转换结果是:

<p><img src="/path/to/img.jpg" alt="Alt 文本" title="标题" /></p>

可以看到图片与链接的引用方式创建时,引用格式是一样的,所以,一个引用可以同时被图片和链接所使用。

添加代码段

添加代码段应该是最简单的了,只需要把你想发布的任何代码不要顶头写就行,一般在前面留四个空格,比如:

我们的代码如下:

    <p>这是HTML代码</p>

转换后得到:

<p>我们的代码如下:</p>

<pre><code>&lt;p&gt;这是HTML代码&lt;/p&gt;
</code></pre>

所有以如特殊符号都会被自动转义。

Markdown 语法进阶

在 Markdown 中嵌入原生HTML代码

在 Markdown 代码中嵌入 HTML代码,如果你想直接在 Markdown 中嵌入HTML代码,那么你只需要将代码直接写在需要的地方即可:

这是一个段落。

<table>
    <tr>
        <td>这是用原生的HTML代码写的表格。</td>
    </tr>
</table>

这是另一个段落。

转换之后得到:

<p>这是一个段落。</p>

<table>
    <tr>
        <td>这是用原生的HTML代码写的表格。</td>
    </tr>
</table>

<p>这是另一个段落。</p>

你还需要注意的一点是,Markdown 是不处理HTML的块级元素中的内容的,比如你可以直接在 <p> 标签中使用 * 号:

<p>这是一个 *段落*</p>

在转换之后还是:

<p>这是一个 *段落*</p>

相反的,HTML的行内标签却可以在 Markdown 的语法的任何地方使用(除了代码段中):

这是一个文本段,<span>这个被span标签</span> 包裹了。

转换后得到:

<p>这是一个文本段,<span>这个被span标签</span> 包裹了。</p>

行内元素包括 span*、*cite*、*del 等。

自动转义特殊字符

在HTML中,有两个字符是十分特殊的,他们是 < 和 & 符号,< 表示一个HTML标签的开始,&表示HTML特殊字符转义符的开始,在 Markdown 中这两个字符将会自动被转义,就如你刚才所看到的这一段话:

在HTML中,有两个字符是十分特殊的,他们是 < 和 & 符号,< 表示一个HTML标签的开始,&表示HTML特殊字符转义符的开始,在 Markdown 中这两个字符将会自动被转义,就如你刚才所看到的这一段话:

Markdown 转换之后得到的将是:

<p>在HTML中,有两个字符是十分特殊的,他们是 &lt; 和 &amp; 符号,&lt; 表示一个HTML标签的开始,&amp;表示HTML特殊字符转义符的开始,在 Markdown 中这两个字符将会自动被转义,就如你刚才所看到的这一段话:</p>

注意出现特殊符号的那几个位置。现在如果你想将一段文本指定到下面的这个URL中:

http://aitine.com/aitine?category=1&tag=3

Markdown 自动将您转换成为:

http://aitine.com/aitine?category=1&amp;tag=3

但是并不任何时候出现这些特殊自符的时候 Markdown 都会转义它们,比如前面说的,如果你写的就是完整的HTML代码,那么 Markdown 会不去管它,同样的,如果你写的是完整的HTML转义符,Markdown 也不会去管它,比如:

&copy;

Markdown 将不会去处理它,但是如果是:

AT&T

则会被转义成为:

AT&amp;T

块级元素

段落与分行

一个段落就是前面都有空行的一行或者多行文本(空行的意思是说你看上去像空行的东西,比如一行内没有任何内容或者一个或者多个空格或者制表符都属于空行),任何段落都不能以空格或者制表符开始。

在 Markdown 中,所谓的一行或者多行是指:没有空行分隔的任意行文本,这个和许多其它 文本至HTML转换工具不同,Markdown 使用的是 _硬包裹_,它不会像其它转换工具那样转换成为 <br /> 标签。

如果你是真的想在一个段落中插入一个 <br /> 标签,那么只需要在你想插入的地方输入两个或者两个以上的空格,然后回车,Markdown 就会在这里强制为你换行。

块级元素是可以嵌套的,比如一个 li 里面可能会嵌套的块引用,这时,块引用所使用的 > 符号应该再缩进了,比如下面这样的:

* 这是第一条引用项目

    > 这是第一条引用的内容

* 这是第二个项目

最终会被转义成为:

<ul>
<li><p>这是第一条引用项目</p>
<blockquote>
<p>这是第一条引用的内容</p>
</blockquote></li>
<li><p>这是第二个项目</p></li>
</ul>

在这里我们还需要注意另外一个问题,根据Markdown 对有序列表的定义,所有以数字后面跟着点开头的一行,都将成为一个有序列表,比如:

1234. 这其实并不是一个有序列表项目

也会被转换成为:

<ol>
<li>这其实并不是一个有序列表项目</li>
</ol>

如果有哪里必须这样的形式开头,你可以使用  这个符号来明确的告诉 Markdown 这里是不需要转换的:

1234. 这其实并不是一个有序列表项目

将被转换成为:

<p>1234. 这其实并不是一个有序列表项目</p>

代码块

对于程序员写作,经常需要在文章中嵌入代码段,在Markdown中,要嵌入代码段,只需要将每一行代码前都至少添加四个空格或者一个制表符(如果你想保证代码展示的方式和你想要的展示样式一样,那么最好每一行代码缩进的间距都是一行,我推荐都使用四个空格),Markdown 会将这样的文本都当作需要展示的文本内容(而不是可执行的代码),如果有需要转义的,比如 < 等都会被转义成为转义符,而整个代码段都会被 <pre> 以及 <code> 标签包裹。

这是一个普通的文本段落。

    <code>但是这一行却会被当作是程序代码</code>

上面的文字转换之后得到:

<p>这是一个普通的文本段落。</p>

<pre><code>&lt;code&gt;但是这一行却会被当作是程序代码&lt;/code&gt;
</code></pre>

如果要在行

分割线

你可以使用下面这种方法在内容中插入分割线 <br /> :   * * *

***

*************

- - -

---------------------

上面的任何一行都可以被转换成为:

<hr />

规则就是使用星号或者减号,排成一排,并且数量超过三个,任何两相相邻的符号之间的距离不能超一个空格

PHP Markdown Extra 介绍 (仅限 PHP Markdown Extra 版本)

上面我们已经完整的学习了 Markdown 官方 Perl 版本的基本使用方法,但是 Markdown 不止Perl一种语言的实现,在任何时候都应该尽可能的先考虑 Markdown 的基本实现,如果基本实现不支持的功能时,再考虑其它的实现,而本站所使用的Markdown为PHP的扩展实现,它在官方版本上,还增加了许多实用的功能,下面对其进行详细的介绍:

HTML 块级元素中的 Markdown 语法

在上面的介绍中我们已经说过,Markdown 是不对HTML块级元素中的内容作任何处理的,所以,你写进一个 <div> 标签中的任何内容都不可能享受到 Markdown 带来的方便,但是在 PHP Markdown Extra(以下简称PME)中,你却可以通过一种方式实现,即在其内容也需要提供 Markdown支持的块级元素标签中加入:markdown=“1” 这个属性和值:

<div markdown="1">
这是一段由 *PHP Markdown Extra* 转换的内容
</div>

将成转换成为:

<p>这是一段由 <em>PHP Markdown Extra</em> 转换的内容</p>

标题元素的ID属性

在PME中,你可以对标题设定ID属性,只需要在标题行在后面加下给大括号包裹的CSS ID选择符即可,比如:

这是文章最上面的标题        {#article-header}
==================

## 这是某一个段落的标题 ##    {#some-part}

输出为:

<h1 id="article-header">这是文章最上面的标题</h1>

<h2 id="some-part">这是某一个段落的标题</h2>

然后,创建了带ID属性的标题之后,我们就可在文章的任何地方使用链接指向该标题了:

<p><a href="#some-part">某段的标题</a></p>

强制代码块

在原生的 Markdown 中,代码块必须以每一行增加统一的缩进(一般为四个或者一个制表符)来表示,但是在PME,还提供了一种强制代码段的实现方式,即在任何要作为代码段的文本前后分别加上三个或者超过三个的“*~*” 字符即可,但必须保证前后符号数量是一样的,使用这种方法创建代码段,代码不再需要缩进:

这是一个普通的段落

~~~~~~~~
这个是一行代码
~~~~~~~~

输出为:

<p>这是一个普通的段落</p>

<pre><code>这个是一行代码
</code></pre>

在下面学习定义列表时,你还可以发现,在定义列表的结尾,必须使用这种方式来创建代码段。

表格

PME 有自己专有的语法来创建简单的表格,“简单”的表格如下:

姓名|性别|年龄
-|-|-
潘韬|非女|24
刘钊|鸟蛋|2x
胡潇|娘子|2x

第一行将创建表格的头部,第二行分隔表格的头部与主体部分,从第三行开始,每一行为一个表格行,列与列之间都是通过“*|*”字符分隔,上面的字符输出的的表格如下:

<table>
<thead>
<tr>
  <th>姓名</th>
  <th>性别</th>
  <th>年龄</th>
</tr>
</thead>

<tbody><tr>
  <td>潘韬</td>
  <td>非女</td>
  <td>24</td>
</tr>
<tr>
  <td>刘钊</td>
  <td>鸟蛋</td>
  <td>2x</td>
</tr>
<tr>
  <td>胡潇</td>
  <td>娘子</td>
  <td>2x</td>
</tr>
</tbody></table>

需要注意的是,表格要求一行中至少有一个 “|*” 字符,这使得如果你想创建一个只有一列的表格,那么必须至少在每一行的前面或者后面或者两头都加上 *| 。

PME 还支持为表格中不同的列指定不同的文本对齐方向,默认的都是向左对齐,如果某一列需要向右对于,只需要在其头部与主体分隔行中,那一列的最后面加上冒号“*:*” 即可,如下示例:

产品 价格
.com域名 120元/年
10G VPS 500元/年
网站建设 根据不同的网站需求报价

输出的代码如下:

<table>
<thead>
<tr>
  <th>产品</th>
  <th align="right">价格</th>
</tr>
</thead>

<tbody><tr>
  <td>.com域名</td>
  <td align="right">120元/年</td>
</tr>
<tr>
  <td>10G VPS</td>
  <td align="right">500元/年</td>
</tr>
<tr>
  <td>网站建设</td>
  <td align="right">根据不同的网站需求报价</td>
</tr>

</tbody></table>

在表格中,你同样还可以使用任何行内语法,如下:

| 函数名称 | 说明                    |
| ------------- | ------------------------------ |
| `help()`      | 展示帮助窗口       |
| `destroy()`   | **摧毁你的电脑**     |

定义列表

PME 对定义列表也提供了支持,定义方式如下:

潘韬
:    有点儿像疯子的神经病

刘钊
:    有点儿像神经病的疯子

胡潇
:    是啥?

输出为:

<dl>
<dt>潘韬</dt>
<dd>有点儿像疯子的神经病</dd>

<dt>刘钊</dt>
<dd>有点儿像神经病的疯子</dd>

<dt>胡潇</dt>
<dd>是啥?</dd>
</dl>

定义列表允许你在定义描述时使用多行,第二行开始不再需要缩进,如下所示的定义描述中,后面两行的效果是一样的:

定义标题
:    定义的第一行
     这是第二行
这是第三行

如果在定义描述中需要分段,则只需要在段与段之间增加一个空行,并且保证第二段的第一行与定义描述的第一行使用同样的缩进即可:

定义标题
:    定义的第一行
     这是第二行
这是第三行

     这是第二段
     这是第二段二行
这是第二段第三行

如果一个定义有多个定义描述,那么只需要使用多个“*:*” 即可:

定义标题
:    定义的第一个描述
:    这是第二个描述

:     这是第三个描述

输出为:

<dl>
<dt>定义标题</dt>
<dd>定义的第一个描述</dd>

<dd>这是第二个描述</dd>

<dd>
<p>这是第三个描述</p>
</dd>
</dl>

从上面的示例中我们可以看到,如果在一个定义标题创建多个定义描述时,如果某一个定义描述前增加一个空行,则这个定义描述中会创建一个段落,否则将直接将内容包裹在 <dd> 标签中。在定义描述中,我们同样还可以添加代码段,列表,或者块级引用等内容,如下示例:

定义标题
:    这是一个定义描述

          <?php print("Hello PME"); ?>

     > 块引用
     > > 块引用中的引用

     1. 有序列表
     2. 第二项

输出结果为:

<dl>
<dt>定义标题</dt>
<dd>
<p>这是一个定义描述</p>

<pre><code>  &lt;?php print("Hello PME"); ?&gt;
</code></pre>

<blockquote>
  <p>块引用</p>

  <blockquote>
    <p>块引用中的引用</p>
  </blockquote>
</blockquote>

<ol>
<li>有序列表</li>
<li>第二项</li>
</ol>
</dd>
</dl>

脚注

脚注的工作原理与引用方式的链接十分的像,一个脚注由两个元素构成:一个对脚注的引用将会在转换的过程中转义为一个上标元素,并且链接至脚注的定义;一个脚注的将会和其它脚本等放在文档的最末位(位置非必需),如下示例:

生前何必贪睡?死后自会长眠![^1]

[^1]:引用自潘韬撰写的《睡眠经》第一章第一节

输出为:

<p>生前何必贪睡?死后自会长眠!<sup id="1"><a href="#fn:1" rel="footnote">1</a></sup></p>

<div>

<ol>

<li id="1">
<p>引用自潘韬撰写的《睡眠经》第一章第一节&nbsp;<a href="#fnref:1" rev="footnote">↩</a></p>
</li>

</ol>
</div>

Plone 主题可以通过 ZMI 直接在后台进行修改,但是这也只能满足一部分需求,如果我们需要更多可定制的操作,那么就需要完全的创建新的主题,在 Plone 中要创建一个主题,很简单。

使用 paster 创建 Plone 主题

Plone 安装完成之后,在 {instance_path}/bin 目录中,就有 paster,通过它,我们可以创建许多东西,比如创建新的 content type,在这里,我们就使用它来创建新的 Plone 主题。

首先,进入 {instance_path}/src/ 目录:

cd /path/to/instance/src

之后运行下列命令:

../bin/paster create -t plone3_theme aitinetheme.aitine

然后脚本会询问你一些问题,你只需要按着你的实际情况以脚本提供的格式回复即可。脚本运行完成之后,就会在 ./src 目录中生成 aitinetheme.titine 目录,这就是新的 plone 主题目录,之后的所有操作都将在该目录中进行。

使用 sc.paster.theme 创建 plone 主题

本文重点将介绍使用 sc.paster.theme 这个工具来创建 plone 主题,虽然和 paster 有点像,但是我个人还是更喜欢这个工具。

安装 sc.paster.theme

该工具并没有默认提供,我们需要先安装它,使用 easy_install 命令即可,在这里,我们有一点需要记住,如果你的 Zope 服务器使用的是自带的 Python ,那么,你不能直接执行 easy_install ,因为这样会将其安装到全局的 Python 环境中,我们需要指出 Zope 服务器所使用的 Python 路径,比如:

/Applications/Plone/Python-2.6/bin/easy_install -U sc.paster.theme

安装需要 gcc 等工具,你需要先安装,如果你在 Mac OS X 下安装,那么需要安装 XCode 命令行工具集才行,如果你是在 Windows 下面安装,对不起,哥真不知道应该怎么整。

在 {instance_path}/bin 目录中创建 sc.paster.theme 脚本的快捷方式

sc.paster.theme 安装完成之后,进入 {instance_path}/bin 目录,创建 sc.paster.theme 的链接文件,我这里使用的是硬链接,当然一定要记住,你需要为其重新命名,因为 Plone 本身已经有了一个 paster 脚本了。

ln /Applications/Plone/Python-2.6/bin/paster ./sc_paster

使用 sc.paster.theme 创建 Plone 主题

与 paster 一样,先进入 src 目录,然后执行下面这行命令:

../bin/sc_paster create -t portal_theme aitine.theme.aitine

该命令同样会询问你一些问题,你一样需要一个一个回答,如果按我创建我的 aitine.theme.aitine这个主题来讲的话,整个命令的运行流程如下:

PanTao-Macbook-Pro:src pantao$ ../bin/sc_paster create -t portal_theme aitine.theme.aitine
Selected and implied templates:
sc.paster.theme#portal_theme Tema visual para projetos Plone 3
Variables:
egg: aitine.theme.aitine
package: aitinethemeaitine
project: aitine.theme.aitine
Expert Mode? (What question mode would you like? (easy/expert/all)?) ['easy']: all
Namespace Package Name (Namespace principal) ['aitine']:
Namespace 2 Package Name (Usualmente o nome do cliente) ['theme']:
Package Name (Nome do projeto) ['aitine']:
Description (One-line description of the project) ['']: A plone theme designed by aitine.com, for Aitine.
Creating template portal_theme
Creating directory ./aitine.theme.aitine
Recursing into +namespace_package+
Creating ./aitine.theme.aitine/aitine/
Recursing into +namespace_package2+
......
Copying LICENSE.GPL to ./aitine.theme.aitine/docs/LICENSE.GPL
Copying LICENSE.txt_tmpl to ./aitine.theme.aitine/docs/LICENSE.txt
Copying setup.cfg to ./aitine.theme.aitine/setup.cfg
Copying setup.py_tmpl to ./aitine.theme.aitine/setup.py
Running /Applications/Plone/Python-2.6/bin/python setup.py egg_info

中间很长一段信息已经被我省略了,之后,我们在 src 目录中,就会得到一个名为:@aitine.theme.aitine@ 的文件夹,它就是我们的主题。之后我们还需要将其添加到 buildout.cfg 文件中,添加方式如下:

eggs =
......
aitine.theme.aitine
zcml =
......
aitine.theme.aitine
develop =
......
src/aitine.theme.aitine

添加完成之后,我们需要更新我们的 instance,然后再以 fg 模工运行 Plone:

PanTao-Macbook-Pro:zinstance pantao$ ./bin/buildout
Develop: '/Applications/Plone/zinstance/src/aitine.theme.aitine'
Uninstalling zopeskel.
......
PanTao-Macbook-Pro:zinstance pantao$ ./bin/plonectl fg
instance: 2012-03-19 20:47:44 INFO ZServer HTTP server started at Mon Mar 19 20:47:44 2012
    Hostname: 0.0.0.0
    Port: 8080
2012-03-19 20:47:45 WARNING SecurityInfo Conflicting security declarations for "setText"
2012-03-19 20:47:45 WARNING SecurityInfo Class "ATTopic" had conflicting security declarations
2012-03-19 20:48:01 INFO Zope Ready to handle requests

当你看到 Zope Ready to handle requests 信息时,你的 Plone 已经以 fg 模式运行成功了,现在你需要进入你的开发站点,启用这个主题即可,以 fg 模式运行时,你对主题的修改能即时的展示在前端。

确实是快要疯了,昨天为 Aitine 重新写了主题,基于 Diazo 的,但是在服务器上面怎么也无法安装,最让人头疼的是,在本地各种环境下都能使用,唯一不能使用的就是在服务器上面,这让人很恼火,昨天一个晚上也没有找哪里出错,因为是ZEO安装吧,Buildout时也不报错,使用启动脚本启动也不报错,但是就是启动不了。

今天找了一个以前在老服务器上可用的Diazo Plone Theme,发现也运行不了,这让我确定,并不是我的主题有问题,然后想到看看是不是服务器环境的问题,因为我就这两天把服务升级到了Ubuntu 64位服务器系统,接着我就把刚刚停用的32位系统启动,把主题在32位系统上面安装,没有什么问题,最终确定了:服务器环境的问题。

知道问题出在服务器上面,就好办事儿了,Diazo主题必须要有LXML支持,在其它环境都一致的情况下,只有可能是这个地方出问题,所以我就把 lxml 的 egg 删除之:

root@aitine:/usr/local/plone/buildout-cache/eggs# rm -rf lxml-2.2.8-py2.6-linux-x86_64.egg/

之后 还需要对 buildout.cfg 文件作如下修改:

[buildout]
parts =
lxml # 保证 lxml 一直在第一位
...

之后重新 Buildout 出来,但是又出问题了,无法 buildout,报下面这个错误:

...
...
An error occured when trying to install lxml 2.2.8. Look above this message for any errors that were output by easy_install.
While:
Installing instance.
Getting distribution for 'lxml==2.2.8'.
Error: Couldn't install: lxml 2.2.8

这个在 这里 找到解决方法:

sudo apt-get install libxml2-dev libxslt-dev

这个安装完成之后,再重新 buildout 即可。