Below is some example text in various styles - how can we get text to display the same way in a TinyMCE editing text box while it's being edited?
Typical paragraph text with bold and a link.
Another paragraph, below is an un-ordered list.
Here's an ordered list:
Create a new CSS file for the theme you're using and add it to the directory of your theme:
themes/example-theme/
or
sites/default/themes/example-theme/ (better)
You can call the new CSS file anything you like, in this tutorial we'll call it format.css.
Log in to your site and go to:
admin/settings/tinymce
Add a profile or edit an existing one. Note: you need to edit all existing TinyMCE profiles.
Scroll down to the 'CSS' fieldset, and enter these values:
Editor CSS: define css
CSS path: sites/default/themes/example/format.css
* whatever the path to the CSS file you added in Step One
* path shouldn't start with a '/'
Submit the form.
TinyMCE is now using the new CSS file that you've added, but what styles should be added to the file?
We want to keep this file as small as possible, so we should only add styles for elements that wil display in the editing text box, such as headings, paragraph and bold styles. We can leave out structural elements such as structuring styles used to create multi-column pages.
Open the style.css file of your theme and copy across styles that format text - quickest way is to copy accross eveything and remove what is not required.
Now the real work begins!
There are some elements that need to be overridden because TinyMCE puts the contents of the editing box in an iFrame. Any elements that rely on inheriting their style from the body element need to be changed like this:
body.mceContentBody
Example:
body h1{
font-family: "Trebuchet MS", Geneva, Arial, Helvetica, SunSans-Regular, Verdana, sans-serif;
margin: 0;
}
Change to...
body.mceContentBody h1{
font-family: "Trebuchet MS", Geneva, Arial, Helvetica, SunSans-Regular, Verdana, sans-serif;
margin: 0;
}
Any element that relies on inheriting a style from .content should work if you remove '.content ' from it.
.content h1 {
color: #369;
font-size: 1.9em;
}
Change to...
h1 {
color: #369;
font-size: 1.9em;
}
Avoid including code in your database.
Common places code is included:
Drupal is a framework with great APIs, instead of writting your own custom code, try to use the built in APIs.
At the top level of Drupal there are two directories:
This seems like the right place to put extra modules and themes, but it's not!
These are for core modules and themes.
When it comes time to do an update to Drupal, your custom and contrib modules and themes would be mixed up with Drupal core, making the upgrade hard work
Add all custom modules and themes to the sites/ directory:
Try to avoid being relient on amendments to third party contributed modules.
Do make modifications to the contrib module, create a patch containing your changes, and file a feature request or bug report with your patch attached. However, in case your patch is not commited to the module by it's maintainer, it's best to also make those modifications through a seperate module.
You should at all costs avoid making any changes to Drupal core.
If your site can be hacked, it will be hacked!
Write documentation for any code you write, and for how your system is set up.
We'll do another tutorial in future on how to thouroughly document your project and the tools available:
Forms API Reference
http://api.drupal.org/api/4.7/file/developer/topics/forms_api_reference.html
Forms API Quickstart Guide
http://api.drupal.org/api/4.7/file/developer/topics/forms_api.html
function legal_display_fields() {
$form = array();
$conditions = legal_get_conditions();
$form['id'] = array(
'#type' => 'value',
'#value' => $conditions['id'],
);
$form['legal'] = array(
'#type' => 'fieldset',
'#title' => t('Terms and Conditions of Use'),
'#weight' => 29,
);
$form['legal']['conditions'] = array(
'#type' => 'textarea',
'#title' => t('Terms & Conditions'),
'#default_value' => $conditions['conditions'],
'#value' => $conditions['conditions'],
'#rows' => 10,
'#weight' => 0,
'#attributes' => array('readonly' => ''),
);
$form['legal']['legal_accept'] = array(
'#type' => 'checkbox',
'#title' => t('Accept Terms & Conditions of Use'),
'#default_value' => 0,
'#weight' => 50,
'#required' => TRUE,
);
$form['#after_build'] = array('legal_preview');
$form['preview'] = array(
'#type' => 'button',
'#value' => t('Preview'),
);
$form['save'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
$output = drupal_get_form('legal_administration', $form);
return $output;
}
function legal_administration_validate($form_id, $form_values) {
if ( empty($form_values['conditions']) ) form_set_error('conditions', t('Terms & Conditions must be entered.'));
return;
}
function legal_administration_submit($form_id, $form_values) {
db_query("INSERT INTO {legal_conditions} (tc_id, conditions, date, extras, changes) VALUES (NULL, '%s', %d, '%s', '%s')", $form_values['conditions'], time(), serialize($form_values['extras']), $form_values['changes']);
drupal_set_message (t('Terms & Conditions have been saved.'));
return;
}
It's important for you and the client to have a clear idea of what is happening at each stage
At this stage you will need:
Not using a computer will usually make the process seem less technical and easier to follow.
Discuss the goals that the client is trying to achive with the project.
To make it easier to discuss the motivations and behaviour of these users, it's useful to create personas for them - give each user type an example person, their name and a short description.
if you can think of a someone you know that fits into a user type that can be very useful.
In practise you will be discussing this at the same time as you define users.
Now that we've discussed with the client the who? what? and how? of their site we can start to model the information.
Model information on computer using a diagramming application
Transfer information from cards into your diagramming application and create a map of relationships.
Decide how features can be implemented in Drupal
Test modules you might use on a development site. This will be the start of the project website.
Convert your features diagram into a site map showing sections and pages.
Then map out how user types relate to each section and page, giving you a clear view of how each user type will navigate to the parts of the site that are relevant to them.
Once we know how all the pages relate to each other we create wireframes for each one.
The user is to be assesed by a property such as:
The realm is a string that's defined to identifying that property, such as:
A condition of a realm.
For example, if realm was 'age' then grant IDs might be:
More than one grant ID can be defined per realm.
Each grant ID can control view, edit and delete options for a node.
Used to define a realm.
Used to check if the user passes conditions specified for a grant ID.
A content type Life Lesson is set up.
Members over 18 years old can:
Members over 30 can:
Members over 50 can:
hook_node_access_records() is used to define who can do what.
hook_node_grants() is used to check if the user is over each age range.
In this example the 'philosophy' realm is set up and three grant IDs are set up:
function moderate_node_access_records($node) {
$grants = array();
if ($node->type == 'life_lesson') {
$grants[] = array(
'realm' => 'philosophy',
'gid' => 1,
'grant_view' => TRUE,
'grant_update' => FALSE,
'grant_delete' => FALSE,
'priority' => 100,
);
$grants[] = array(
'realm' => 'philosophy',
'gid' => 2,
'grant_view' => TRUE,
'grant_update' => TRUE,
'grant_delete' => FALSE,
'priority' => 100,
);
$grants[] = array(
'realm' => 'philosophy',
'gid' => 3,
'grant_view' => TRUE,
'grant_update' => TRUE,
'grant_delete' => TRUE,
'priority' => 100,
);
}
return $grants;
}
function moderate_node_grants($account, $op) {
$grants = array();
if ($account->age >= 18 ) {
$grants['philosophy'][] = 1;
}
if ($account->age >= 30 ) {
$grants['philosophy'][] = 2;
}
if ($account->age >= 50 ) {
$grants['philosophy'][] = 3;
}
return $grants;
}
Drupal deals with content, logic and presentation in separate layers.
The theme layer controls the look of how a Drupal site's content will be displayed.
The name of the theme directory is the name of the theme.
The easiest way to create a theme is to make a copy of an existing theme, such as Bluemariene, then rename the new directory. Drupal will use the name of the new copy as the name of your new theme.
The new theme is now ready to be edited.
page.tpl.php defines the structure of the page that is displayed
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="<?php print $language ?>" xml:lang="<?php print $language ?>">
<head>
<title><?php print $head_title ?></title>
<?php print $head ?>
<?php print $styles ?>
<script type="text/javascript"><?php /* Needed to avoid Flash of Unstyle Content in IE */ ?> </script>
</head>
<body>
<table border="0" cellpadding="0" cellspacing="0" id="header">
<tr>
<td id="logo">
<?php if ($logo) { ?><a href="<?php print $base_path ?>" title="<?php print t('Home') ?>"><img src="<?php print $logo ?>" alt="<?php print t('Home') ?>" /></a><?php } ?>
<?php if ($site_name) { ?><h1 class='site-name'><a href="<?php print $base_path ?>" title="<?php print t('Home') ?>"><?php print $site_name ?></a></h1><?php } ?>
<?php if ($site_slogan) { ?><div class='site-slogan'><?php print $site_slogan ?></div><?php } ?>
</td>
<td id="menu">
<?php if (isset($secondary_links)) { ?><div id="secondary"><?php print theme('links', $secondary_links) ?></div><?php } ?>
<?php if (isset($primary_links)) { ?><div id="primary"><?php print theme('links', $primary_links) ?></div><?php } ?>
<?php print $search_box ?>
</td>
</tr>
<tr>
<td colspan="2"><div><?php print $header ?></div></td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" id="content">
<tr>
<?php if ($sidebar_left) { ?><td id="sidebar-left">
<?php print $sidebar_left ?>
</td><?php } ?>
<td valign="top">
<?php if ($mission) { ?><div id="mission"><?php print $mission ?></div><?php } ?>
<div id="main">
<?php print $breadcrumb ?>
<h1 class="title"><?php print $title ?></h1>
<div class="tabs"><?php print $tabs ?></div>
<?php print $help ?>
<?php print $messages ?>
<?php print $content; ?>
</div>
</td>
<?php if ($sidebar_right) { ?><td id="sidebar-right">
<?php print $sidebar_right ?>
</td><?php } ?>
</tr>
</table>
<div id="footer">
<?php print $footer_message ?>
</div>
<?php print $closure ?>
</body>
</html>
In template.php, custom theme overrides can be added:
function phptemplate_xxxxxx($user, $image) {
return _phptemplate_callback('xxxxxx', array('user' => $user, 'image' => $image));
}
A file called xxxxxx.tpl.php can then be created to customise this element.
<div id="something">
<?php print $user ?>
<?php print $image; ?>
</div>
<?php print $content; ?>
For the sidebar:
<?php print $sidebar_left; ?>
For the primary links menu:
<?php if (isset($primary_links)) : ?>
<?php print theme('links', $primary_links, array('class' => 'links primary-links')); ?>
<?php endif; ?>
For the CSS:
<?php print $styles; ?>
in the head of the document.
This led to a mini-rant by myself about omitting all the unnecessary styles that Drupal adds by doing this:
/style.css" type="text/css" media="screen" charset="utf-8" />
Chris sensibly suggested using drupal_add_css() (http://api.drupal.org/api/function/drupal_add...), which seems a more Drupal way of doing things:
<?php
/**
* adding and removing style sheets
* this code goes in your template.php file
*/
function _phptemplate_variables($hook, $vars) {
switch ($hook) {
// we want this code to run when this hook is called by a page
case 'page':
/**
* drupal builds an array of style sheet info during a callback
* this array is later turned into style sheet imports with the
* drupal_get_css() function -> http://api.drupal.org/api/function/drupal_get...
* and finally it all get's printed in page.tpl.php with '<?php print $styles; ?>'.
*/
/**
* We can add style sheets here using the function
* drupal_add_css() -> http://api.drupal.org/api/function/drupal_add...
* and remove styles by unset(ting) the appropriate array elements.
*/
/**
* example #1 - add a css file called layout.css that's located
* in our template folder - path_to_theme() does exactly what
* you'd expect -> http://api.drupal.org/api/function/path_to_th...
*/
drupal_add_css(path_to_theme() .'/layout.css', 'theme', 'all', TRUE);
/**
* example #2 - remove a single style sheet - in this case system.css
* calling drupal_add_css without any parameters will
* return the style sheet array
*/
$css = drupal_add_css();
// print the array - make a note of what you want to remove
//print_r($css);
// use the php function unset -> http://uk.php.net/manual/en/function.unset.php
unset($css['all']['module']['modules/system/system.css']);
/**
* example #3 - remove all style sheets added by modules
* un-comment below
*/
//unset($css['all']['module']);
/**
* finally over-ride our $vars['styles'] variable (which will become $styles in
* page.tpl.php) by passing our amended style sheet array through drupal_get_css()
* and we're good!
*/
$vars['styles'] = drupal_get_css($css);
break;
}
// don't forget to return the $vars array :)
return $vars;
}
More variables here: http://drupal.org/node/11812
Things change a bit in Drupal 6: I'll give a talk on this in a month or two. This tutorial will show you how to use the nodequeue module on your site. The initial part of the tutorial will show manual use of the module and the later part will show how nodequeue can be integrated with actions and workflow modules.
We will build a block showing the 5 latest story posts. Initially we will add them manually, then we'll do it automaticallly, we'll add new Stories to our queue and then remove them at a predefined time in the future. (and on the way we'll show you how to create views of a nodequeue).
For some background on why nodequeue is important, read Merlin's post on his site.
You will need
You can get the Drupal modules from the links below:
Download and install on your site. Need help installing contributed modules? look at the handbook page
Enable the modules under admin/build/modules
Go to admin/content/nodequeue where we can add and list nodequeues.
Click Add. Then enter the following values in the fields
Title: Latest Stories
Queue Size: 5
Link 'add to queue' text: Add Story
Link 'remove from queue' text: Remove Story
Roles: Click roles who are allowed to add/remove stories from the list
Types: Click Story
Click the Submit button
We should now see our listed with a title 'Latest Stories', Max Nodes 5, and In queue 0.
Now let's add some Stories to our nodequeue...
Create a node of Content type Story. For example call it Story 1, add some body text and hit submit.
As you can see below we have an Add Story link showing. Click on it and it will cause the story to be added to our nodequeue.
Once clicked, notice how the link changes to Remove Story.
Now if we click on the Node queue link we can see that the node is on the queue.
We can view the queue, which will allow us to maintain position and what nodes to show in the queue. The nodequeue could be used for listing your favourite nodes, the nodequeues are system wide as Earl has mentioned so currently no user level nodequeues are possible.
Go ahead and add two more Story nodes, perhaps imaginatively called Story 2 and Story 3.
You may think that you just need to go to admin/build/block in order to add the nodequeue as a block to your site. That's not the case, first we need to enable the nodequeue in Views, so go to admin/build/views and you will see nodequeue_1 listed (ok, mine says nodequeue_2 well I did a dry run before writing this tutorial) with Title 'Latest Stories', notice how the Status is Disabled.
Click on the Enable link under the Actions column in order to enable it.
Now go back to admin/build/block and you will see the nodequeue listed under the disabled.
Select the region where you want it to be displayed and click Save Blocks button. (Note we can configure the block which will allow us to override the Title, and do all the usual things possible with Blocks permissions etc). You should now see the nodequeue appear as a block in your chosen region.
So now we have a Block on the site which lists the content of our nodequeue, we can manually add and remove Story nodes from the queue. But what happens when we eventually have more than 5 Story nodes on our queue? The oldest story will disappear from the queue and we'll have a more link displayed at the foot of the block.
Also, if you inspect the nodequeue the In queue column will show 5 and the words QUEUE FULL.
Before we use Actions and Workflow modules, a few observations about nodequeue:
Let'smake a view of our nodequeue, so we can make the Latest Stories block list stories by the time the Story node was created.
So we have a Latest Stories Block but it does not list our Stories according to the creation time of the Stories. So what do we do? Well we go back to the Views page and make a view of our nodequeue.
Go to admin/build/views then click the Add link under the Action column for nodequeue_1
Now we are in the Views edit form, scroll down to the Sort Criteria, you will see the default sort order for a nodequeue.
Change it to this, and then we will get the desired block behaviour.
Now Save, and we'll see that the nodequeue has been overridden, click on the link under the URL column to see the block. The block will also appear under admin/block menu.
So let's push on and use Actions and Workflows modules to add and remove items from the queue automatically. I think now you are starting to see the power of the nodequeue module that Earl Miles was writing about.
Earl Miles also says he hasn't tried nodequeue with the actions and workflow module. I've used those modules so thought I'd put nodequeue through its paces.
I am assuming that you have installed actions and workflow module as documented earlier in this tutorial. So what we'll now do is
Go to admin/build/actions and look at the entries in the select list, you'll see we now have Add to Node Queue and Remove from Node Queue actions. Select Add to Node Queue and click the Add New Action button. Then you will see the following and we can select the desired nodequeue, which is Latest Stories.
Click Save, and then repeat the process for adding the Remove from Node Queue action. Then we should end up with the two new actions, hightlighted in red below.
So now we have the nodequeue actions at our disposal, we can turn to the workflow module. We need to create a workflow, define states for the workflow, then finally add that workflow to a node or node types.
Go to admin/build/workflow and click Add Workflow link.
Define the workflow name as Stories then click the Add Workflow button.
Now you will see we have the workflow and we can perform some operation on that workflow. A workflow is a series of states, probably the most simple workflow would have 2 states, on and off. For our tutorial we will use two states called Added and Expired. The idea being that we want to add stories to our Latest stories list and then change their state to expired at a chosen date and time in the future.
Let's add the states to our workflow.
Now define the transitions. I'm not going to get bogged down with the detail here, I think transition logic is self explanatory.
Define Workflow actions
Once defined it should look like this.
Make sure the workflow is defined for the Story nodetype.
Now let's add a Story. Once you've added a story it should appear on the queue. Also if you Edit the story you'll notice we have Workflow and Node Queue links.
Let's define an expiry time. To do this click on the Workflow link for the Story node, set Change Stories state to Removed, and Schedule for state change at (specify some datetime in the future). (It would have been nice if we could specify an elapsed time from the creation date of the node). Click submit, then once the scheduled state change time is reached the node should disappear from the node queue, (Note the scheduled transition only happens upon the next execution of cron.php).
That about wraps up this tutorial, please post comments if you spot any errors, or ways it can be improved. Enjoy.
Ever since I started using Drupal in October 2005 I wanted to be able to walk the code and debug quickly and efficiently. There is only so much you can do with drupal_set_message and dprint_r statements, so I looked around and decided to try Activestate Komodo. I hit a few problems along the way in configuring my Mac, I think a previous evalulation copy of Zend IDE and my relative newness to OS X were the problem. But with some background reading, and some help from JeffG at Activestate I got everything working nicely.
This tutorial will show you how to configure and use Activestate Komodo to debug Drupal.
Let's look at the whole picture of what we are trying to do and what software we will be using to achieve our aim of debugging Drupal. For simplicity I am going to assume we are going to use a local web server and debug locally on the same machine.
To do this on OS X, the easiest way is to download the Apache 1.3 / PHP 4.3.11 installer package from entropy.ch as the standard version that comes with Tiger does not work.
There are a number of steps to ensuring we get all the applications communicating with each other.
Let's see if it works. There is a utility on the Start Page of Komodo for doing this, however we will also look at the php.ini file that the wizard created and add a few more parameters to get it all working.
To start with, we can use the Check Configuration feature on the Komodo Start Page:
Click on the Check Configuration link and if all is well you should see something like the following:
There are two ways to invoke the debugger.
The first method is also the easiest. Simply, navigate to the page you wish to debug, append the &XDEBUG_SESSION_START=userid where userid is your user name, sp in my case I substitute userid with bpretsel, hit the browser refresh button and then the Komodo should start debugging. Switch to Komodo, also check that the listener has detected the call to debug.
Now, if it does not work then the problem may be one of the following:
The following commands are common in most debuggers, and anyone who has used a debugger in another language, e.g. C, Perl will feel at home.
These are reading from left to right: Step In , Step Over, Step Out, Go/Continue, break now, Stop, Detach
Step In
This will follow the code, so that if you step into a line containing a require statement or a function call, pressing this button will cause the debugger to go to the include file for a require statement, or the file containing the function definition for function calls.
Step Over
This will not follow the code, it will step over to the next line to execute. This is the most common action, and certainly you'd generally want to step over require statements.
Step Out
Allows you to step out of a function or include file. Useful if you accidentally stepped into something or you've seen enough and want to return to where the include file or function was called from.
Go/Continue
Usually you would use this one if you have some breakpoints set up. Under such circumstances you want to let the debugger continue execution as you know the debugger will halt when the next break point is reached.
Stop
Stop debugging. You may have seen enough to know the case of your problem or you are just fed up!
Detach
Stop the debugging process, but instead of stopping continue execution of the program.
Going through every line of code is not the most efficient way to debug, sooner than than later you'll want to start debugging at a particular line in a file or function, eventually with practice you'll be wanting to start debugging when certain criteria has been met.
Load the file you want to debug, if it is not already loaded in Komodo.
Drupal has some pretty big nested data structures, so this feature is a nice way to see those values. Click on the Locals tabpage.
Watching variables is a common task programmers wish to employ when debugging. Usually the scenairo is that you want to know why a certain variable gets a certain value.
To add a variable to the watch list, first select the Watch tab on the bottom left of Komodo. Now click the square dotted icon with the yellow highlight on the top left of it. This will bring up the following dialog. In here type the variable name you wish to add to the watch list.
a Call Stack is a special stack which stores information about the functions/subroutines in a computer program which are currently being executed. It is a stack because when one function calls another, rather than simply jumping to another part of the program.
We can see a call stack in Komodo. Clicking on entries in the Call Stack will cause the text editor section to jump to that particular function definition.
Here we can see that the main section in file index.php at line 15 called the function menu_execute_active_handler in file menu.inc at line 396. The Call Stack helps you to follow the flow of execution of the program.
Komodo is a good way to browse the php code, and functions in particular.
Then when you click on function, you can view the comments for that function. Notice the search form, typing in part of the function name will filter the window to only show functions which contain the search text. Here I typed 'cache'...
To remove breakpoints , view the current breakpoints in the Breakpoints tabpage. Click on the breakpoint you wish to remove and then click on the red circle icon with a black cross. To delete all breakpoints click on the icon with two red circles and a black cross. To toggle whether a breakpoint is active or not, click on the icon with the red and white circle.
Get products and technologies
Other links on this subject