Recently in Web Development Category

This is a solution to mix public downloads and protected folders.

For this, you will need CCK, specifically CCK FileField and Content Permission. Also mod_rewrite should be enabld.

First, add a new file field to a content type, and configure its path to the directory that will be protected. For example, if /files/protected is the directory that will contain protected files, then you can either set that as the path or a folder within it.

If you have already set /files as your file system path, then there should be a .htaccess file created by drupal. You should copy that into each of the protected folders. If you don't, you may get 404 errors.

Open the .htaccess file in your root drupal directory, add the following line to the end of mod_rewrite rules:

RewriteRule ^files\/(protected\/.*)$ index.php?q=system/files/$1 [L,QSA]

Replace protected with your top-level protected folder name. If you have other custom rewrite rules you should be more careful about where you put the above line. Putting it at the end works for me.

Lastly, on your 403 page, you can examine if user is requesting a file in a protected directory using request_uri() and strpos().

Customize Ubercart Product Page

| | Comments (0)

Here's a little snippet of code to customize ubercart product page template so that the contents are grouped into tabs.

You need Tabs and CCK Fieldgroups Tabs.

Go to yoursite/admin/content/node-type/product/fields and add some cck groups and fields, then display them properly (tabs for full node).

Create node-product.tpl.php. Paste the following php code in (enclose it with php tags):

$form = array();
$form['ptabs'] = array(
'#tabset_name' => 'ptabs',
'#type' => 'tabset',
);
$i = 0;

// shows a summary of the product
$summary;
$summary .= $node->content['weight']['#value'];
$summary .= $node->content['dimensions']['#value'];
$summary .= $node->content['body']['#value'];
$form['ptabs'][$i] = array(
'#type' => 'tabpage',
'#title' => t('Summary'),
'#content' => $summary,
);
$i++;

// add other tabs if applicable
if (!empty($node->content['fieldgroup_tabs'])) {
	foreach ($node->content['fieldgroup_tabs'] as $key => $value) {
		if (is_array($value)) {
			if ($value['#type'] == 'tabpage') {
				$form['ptabs'][$i] = array(
				'#type' => 'tabpage',
				'#title' => $value['group']['#title'],
				'#content' => $value['group']['#children'],
				);
				$i++;
			}
		}
	}
}

The page should always have a tab called Summary, and other tabs that you added for the content type if they are not empty (cck fieldgroup tabs module validates this). If you don't wish to have the Summary tab, simply delete that part of code.

Feel free to share the code.

For more info on Dovecot: http://wiki.dovecot.org/LDA

Add the following at the bottom of master.cf

# spamassassin then dovecot LDA
dovecot    unix  -       n       n       -       -       pipe
    flags=DRhu user=vmail:mail argv=/usr/bin/spamc -u randomuser -e /usr/libexec/dovecot/deliver -f ${sender} -d ${recipient}

Basically, postfix pipe the mail into spamc, and since you don't want to run it as root you use another user (-u). -e redirects the output from spamc for dovecot to deliver. And postfix tells dovecot who the sender is, and who the recipient is.

Add the following to main.cf

mailbox_command = /path/to/dovecot/deliver
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1

Now just restart postfix and you are done. Depending on how you setup the virtual domain database, it works for multiple domains.

Webform is a great module, but it doesn't send confirmation email, so here is a quick fix if you are running on Drupal 6.x.

To get the submitted value of a form component, you can use $form_values['submitted_tree']['component_key'] where component_key should be replaced by your component key. If the component is in a fieldset, access it by $form_values['submitted_tree']['fieldset_key']['component_key'] where fieldset_key should be replaced by your fieldset key.

To send the email I chose to use drupal_mail() function, of course there are other ways.

Here is an example:

$from = "example@example.com";
$to = $form_values['submitted_tree']['email'];
$user = $form_values['submitted_tree'['name'];
$body = "Hello " . $user . ",\nThis is a message.";
drupal_mail('webform_extra', 'confirmation_key', $to, language_default(), array('body' => $body), $from, TRUE);
function webform_extra_mail($key, &$message, $params) {
$message['subject'] = "Confirmation email";
$message['body'] = $params['body'];
}

This document is based on the following documents:

This document describes the process of setting up a mail server on Fedora 10. The mail server uses dovecot for authentication and postfix for mail delivery. Postfix Admin is used to manage virtual domains and virtual users. Roundcube Mail is used for user to access their email accounts online. Of course everything depends on apache server and mysql.

Software packages used (dependencies not listed here): httpd, mysql, mysql-server, php, php-mysql, postfix, dovecot

Notable dependencies: php-imap, dovecot-mysql

Roundcube Mail have a lot dependencies, such as php-xml, php-xmlrpc and etc.

Configure Apache Server

# nano /etc/httpd/conf/httpd.conf

In order to reduce the memory usage and make our server more efficient, change the following:

Timeout 20
KeepAlive On
MaxKeepAliveRequests 200
KeepAliveTimeout 4
<IfModule prefork.c>
StartServers	   1
MinSpareServers    1
MaxSpareServers    5
ServerLimit	  50
MaxClients  	  50
MaxRequestsPerChild  2000
</IfModule>

We don't want indexing at all:

<Directory />
    Options -Indexes
    AllowOverride None
</Directory>

Now we are ready to start the server:

# service httpd start

Configure MySQL

After installing mysql, we must change the root password first:

# mysqladmin -u root password  'yourpassword'

Now we manage mysql databases using root login:

# mysqladmin -u root -p

If we are running tight on memory we should use small config file for mysql. And we would want to skip innodb and bdb too.

# cp /usr/share/mysql/my-small.cnf /etc/my.cnf

Now we are ready to start mysql server:

# service mysqld start

Configure Postfix Admin

The reason why we setup postfix admin first is because postfix gets the list of virutal users from mysql database, which can be easily edited by postfix admin. Remember that postfix admin uses innoDB (edit /etc/my.cnf accordingly)

Move into the directory you want to put postfix admin at.

# cd /var/www/html/

Get the latest release from SVN:

# svn co https://postfixadmin.svn.sourceforge.net/svnroot/postfixadmin/trunk postfixadmin 

Create a new user for postfix admin and grant access to mysql:

mysql> CREATE USER 'postfix'@'localhost' IDENTIFIED BY 'password';
mysql> GRANT ALL PRIVILEGES ON postfix.* to 'postfix'@'localhost';

Edit config.inc.php.

# nano config.inc.php

Edit the following:

$CONF['configured'] = true;
$CONF['database_type'] = 'mysql';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfix';
$CONF['database_password'] = 'password';
$CONF['database_name'] = 'postfix';
$CONF['database_prefix'] = '';

And other things you may want to replace with your own.

Now we're ready to setup postfix admin. Go to http://localhost/postfixadmin/setup.php

Follow the instructions on that page. Fairly straight forward.

Configure Directories And Users

At this point we should think about where and how we are going to store mails and manage user accounts. Obviously in this case we will be using mysql, but there are other options too. Now let's create a user that will be used for all virtual users to login and store their mails.

# mkdir -p /home/vmail
# useradd -r -u 5000 -g mail -d /var/vmail -s /sbin/nologin -c "Virtual mailbox" vmail
# chown vmail.mail /home/vmail

Configure Dovecot

Now edit dovecot config file at /etc/dovecot.conf. I prefer nano.

SSL setup:

protocols = imap pop3 imaps pop3s
listen = *
ssl_listen = 
ssl_disable = no
ssl_cert_file = /etc/pki/dovecot/certs/dovecot.pem
ssl_key_file = /etc/pki/dovecot/private/dovecot.pem

Set the local user dovecot will use.

mail_location = maildir:/home/vmail/%u
first_valid_uid = 5000
last_valid_uid = 5000
first_valid_gid = 12
last_valid_gid = 12

Settings for protocols.

protocol imap {
  mail_plugins = quota imap_quota trash expire
  imap_client_workarounds = outlook-idle delay-newmail
}
protocol pop3 {  
  pop3_uidl_format = %08Xu%08Xv
  mail_plugins = quota expire
  pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
}

Authentication settings so that dovecot and postfix can work together:

auth_executable = /usr/libexec/dovecot/dovecot-auth
auth_worker_max_count = 30
auth default {
  mechanisms = plain login
  passdb sql {
    args = /etc/dovecot/sql.conf
  }
  userdb sql {
    args = /etc/dovecot/sql.conf
  }
  user = nobody
  count = 1
  socket listen {
    master {
      path = /var/run/dovecot/auth-master
       mode = 0600
       user = vmail
    }
    client {
      path = /var/spool/postfix/private/auth
      mode = 0660
      user = postfix
      group = mail
    }
  }
}

Settings for plugins:

plugin {
  quota = maildir:User quota
  quota_rule = *:storage=1048576
  quota_rule2 = Trash:storage=102400
  trash = /etc/dovecot/trash.conf
}

Now create the files used in dovecot.conf. First create sql.conf, it's used to read username and password information from the postfix admin database. Basically the file just contains 2 SQL query for dovecot to execute.

Remember to change the database, username and password according to your setup.

driver = mysql
connect = host=localhost dbname=postfix user=dovecot password=password
default_pass_scheme = md5-crypt

user_query = SELECT '/home/vmail/%u' as home, 'maildir:/home/vmail/%u' as mail, 5000 AS uid, 12 AS gid, concat('*:storage=', quota, 'M') AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'

password_query = SELECT username as user, password, '/home/vmail/%u' as userdb_home, 'maildir:/home/vmail/%u' as userdb_mail, 5000 as userdb_uid, 12 as userdb_gid FROM mailbox WHERE username = '%u' AND active = '1'

Create trash.conf.

1 Spam
2 Trash

There are lots of tutorials on generating SSL certificates, so I'm not going to repeat the whole process here. You can run the command below and set postfix to use the same ones:

# openssl req -new -x509 -nodes -out /etc/pki/dovecot/certs/dovecot.pem -keyout /etc/pki/dovecot/private/dovecot.pem -days 3650

Creating your own CA (for postfix):

# openssl req -new -x509 -extensions v3_ca -keyout cakey.pem -out cacert.pem -days 3650

Configure Postfix

Ok, here is my postfix setup:

broken_sasl_auth_clients = yes
home_mailbox = Maildir/
invalid_hostname_reject_code = 554
mail_owner = postfix
multi_recipient_bounce_reject_code = 554
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
myhostname = mail.example.com
mynetworks = 127.0.0.0/8
non_fqdn_reject_code = 554
queue_directory = /var/spool/postfix
readme_directory = /usr/share/doc/postfix-2.5.6/README_FILES
relay_domains_reject_code = 554
sendmail_path = /usr/sbin/sendmail.postfix
smtp_tls_note_starttls_offer = yes
smtp_tls_session_cache_database = btree:${queue_directory}/smtp_scache
smtp_use_tls = yes
smtpd_delay_reject = yes
smtpd_helo_required = yes
smtpd_helo_restrictions = permit_mynetworks, check_helo_access hash:/etc/postfix/helo_access, permit
smtpd_recipient_restrictions = reject_invalid_hostname,            reject_unknown_recipient_domain, reject_unauth_pipelining, permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_rbl_client cbl.abuseat.org, reject_rbl_client rabl.nuclearelephant.com, reject_rbl_client bl.spamcop.net, permit
smtpd_sasl_auth_enable = yes
smtpd_sasl_authenticated_header = yes
smtpd_sasl_local_domain = 
smtpd_sasl_path = private/auth
smtpd_sasl_security_options = noanonymous
smtpd_sasl_type = dovecot
smtpd_tls_CAfile = /etc/postfix/ssl/cacert.pem
smtpd_tls_auth_only = no
smtpd_tls_cert_file = /etc/pki/dovecot/certs/dovecot.pem
smtpd_tls_key_file = /etc/pki/dovecot/private/dovecot.pem
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_database = btree:${queue_directory}/smtpd_scache
smtpd_tls_session_cache_timeout = 3600s
smtpd_use_tls = yes
tls_random_source = dev:/dev/urandom
unknown_address_reject_code = 554
unknown_client_reject_code = 554
unknown_hostname_reject_code = 554
unknown_local_recipient_reject_code = 554
unknown_relay_recipient_reject_code = 554
unknown_virtual_alias_reject_code = 554
unknown_virtual_mailbox_reject_code = 554
unverified_recipient_reject_code = 554
unverified_sender_reject_code = 554
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf
virtual_gid_maps = static:12
virtual_mailbox_base = /home/vmail
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_limit = 51200000
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 5000
virtual_uid_maps = static:5000

Now let's create the sql query files for postfix.

Create mysql_virtual_alias_maps.cf

user = postfix
password = password
hosts = localhost
dbname = postfix
table = alias
select_field = goto
where_field = address

Create mysql_virtual_domains_maps.cf

user = postfix
password = password
hosts = localhost
dbname = postfix
table = domain
select_field = domain
where_field = domain
additional_conditions = and backupmx = '0' and active = '1'

mysql_virtual_mailbox_maps.cf

user = postfix
password = password
hosts = localhost
dbname = postfix
table = mailbox
select_field = maildir
where_field = username

To enable smtpds for outgoing mail server, you need to edit /etc/postfix/master.cf. Just uncomment the following lines:

smtps     inet  n       -       n       -       -       smtpd
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

Then add the following at the end of the file for dovecot to deliver mails into users' inboxes:

# Dovecot LDA
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:mail argv=/usr/libexec/dovecot/deliver -d ${recipient}

Configure RoundCube Mail

First, install all the dependencies that roundcube mail need.

To begin the setup, just browse to http://localhost/roundcubemail/ and follow the instructions there. However, there are certain things need to be edited manually after the 2 configuration files are generated. Since we're using Maildir style inbox, we want to let RoundCube Mail know that.

$rcmail_config['imap_root'] = 'INBOX';
$rcmail_config['imap_delimiter'] = '.';

And set this too so that RoundCube Mail creates all the necessary folders for us:

$rcmail_config['create_default_folders'] = TRUE;

Install Postfix Admin Patch for RoundCube Mail

Download the patch here

RCPFA patches roundcube mail so that it can modify the database that Postfix Admin uses. Extract rcpfa to the root directory of roundcube mail. Then move into the rcpfa directory.

# chmod +x INSTALL.TXT
# ./INSTALL.TXT

Then edit the config files of roundcube mail to change database login info.

That's it.

Hands-On Experience of Symphony

| | Comments (0)

During the past week I have been developing a website for my school's photography club. It's a blog-like photo gallery thats supports multiple authors, I also implemented author registration for the sake of convenience.

After looking at MovableType (and its plugins), TYPO3 (and its extensions), ExpressionEngine, and WordPress, I decided to go with Symphony. The main reason I chose Symphony is because of its extremely user-friendly and minimalistic backend interface. So people who are not-so-good-at-computers won't get totally confused. Believe me, there are people who don't know how to post a photo in WordPress; user-friendliness is important.

However, I have never had any experience with XSL or XSLT, so I spent half of the development time learnig XSLT. But it is easy to learn since it doesn't really have complicated functions and whatnot. BTW, I know Symphony beta 2.0 is out but since I don't have the time to constantly maintain the website I used 1.7.

Here is a list of functions I added/modified, in addition to the default ones.

  • Homepage as an index of photos
  • Categories of photos
  • Featured photo
  • Pagination
  • Author Registration

Started by off by adding Custom Fields(CF) and pages. I was a bit confused with Data Sources and Utilities at the beginning and wasted some time (didn't read any documentation but who reads them anyway?). Applied my limited knowledge of XSLT when editing pages and utilities. Eventually I added new controllers to work with pages. It took me some time to realize that Utilities must associate with Data Source to work, and Controllers pull data from Elements, which you can choose which ones to include.

This page is extremely helpful, and also search in the overture forum when you have problems, searching in Google is not really helpful.

其实很简单, in-genia 早就发布了英文版的集成包, 只需要运行一个安装程序, mysql 和 php 以及 typo3 就都安装在本地电脑上了。下载链接在 这里 。最新的版本在最底部(德国人的奇怪思维)。

软件主要是通过MAMP来运行的, 该软件还包括一个简单的widget, 可以控制mysql服务器的运行以及打开主程序界面 (一个网页而已)。 php可以选择 php4 或 php5。

今天成功地用TYPO3的TypoScript和Spry Accordion Widget(当然还有CSS和JS)结合在一起实现了动态(会动的!)菜单。TypoScript的确是很强大,不过在 TSref 里找东西有点烦人,而且也没有很完整的教程,目前我还在处于瞎子状态中。Google找了半天发现德文资料居多,比英文还多,这倒是头一次碰到。下面是昨天发现的几个还算有点用的网站:

下面是setup的部分代码

marks.ACCORDION = HMENU
 marks.ACCORDION.special = directory
 marks.ACCORDION.special.value = 48
 marks.ACCORDION.1 = TMENU
 marks.ACCORDION.1.target = page
 marks.ACCORDION.1 {
  expAll = 1
  noBlur = 1
  wrap = |
  NO {
   wrapItemAndSub = <div class="AccordionPanel">|</div>
   linkWrap = <div class="AccordionPanelTab">|</div>
   doNotLinkIt = 1
  }
 }
 marks.ACCORDIONL.2 = TMENU
 marks.ACCORDIONL.2 {
      wrap = <div id="AccordionPanelContent1" class="AccordionPanelContent"><ul class="list1">|</ul></div>
  target = page
  noBlur =1
  NO {
   RO = 1
   ATagBeforeWrap = 1
   allWrap = <li>|</li>
   linkWrap= |
  }
 }

用.special属性生成只有pid=48的网页目录,然后生成第2级目录。

下面是template文件的代码:

<div id="Accordion1" class="Accordion">
###ACCORDIONL###
</div>

TypoScript还算比较容易上手,有1个星期基本上就没问题了。不过TYPO3实际用起来还没经验,估计要等到1,2个月后才知道了。