$typename) {
$items['admin/content/node-type/'. str_replace('_', '-', $type) .'/edit'] = array(
'title' => 'Edit',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/content/node-type/'. str_replace('_', '-', $type) .'/profile'] = array(
'title' => 'Content profile',
'description' => 'Configure the display and management of this content profile.',
'page callback' => 'drupal_get_form',
'page arguments' => array('content_profile_admin_settings', $type),
'access callback' => 'user_access',
'access arguments' => array('administer nodes'),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
}
foreach (content_profile_get_types('names') as $type => $type_name) {
$items['user/%user/profile/'. $type] = array(
'title callback' => 'check_plain',
'title' => drupal_ucfirst($type_name),
'page callback' => 'content_profile_page_edit',
'page arguments' => array($type, 1),
'access callback' => 'content_profile_page_access',
'access arguments' => array($type, 1),
'weight' => content_profile_get_settings($type, 'weight'),
'file' => 'node.pages.inc',
'file path' => drupal_get_path('module', 'node'),
'type' => content_profile_get_settings($type, 'edit_tab') == 'top' ? MENU_LOCAL_TASK : MENU_CALLBACK,
);
}
return $items;
}
/**
* Implementation of hook_menu_alter().
* Take over menu items generated by the user module for our categories.
*/
function content_profile_menu_alter(&$items) {
foreach (content_profile_get_types('names', 'edit_tab', 'sub') as $type => $type_name) {
if (!empty($items['user/%user_category/edit/'. $type])) {
$item = &$items['user/%user_category/edit/'. $type];
$item = array(
'page callback' => 'content_profile_page_edit',
'page arguments' => array($type, 1),
'access callback' => 'content_profile_page_access',
'access arguments' => array($type, 1),
'file' => 'node.pages.inc',
'file path' => drupal_get_path('module', 'node'),
) + $item;
}
}
}
function content_profile_page_access($type, $account) {
if ($node = content_profile_load($type, $account->uid)) {
return node_access('update', $node);
}
// Else user may view the page when they are going to create their own profile
// or have permission to create it for others.
global $user;
if ($user->uid == $account->uid || user_access('administer nodes') ){
return node_access('create', $type);
}
return FALSE;
}
/**
* Presents a node editing or adding form for the given content profile.
*/
function content_profile_page_edit($type, $account) {
drupal_set_title(check_plain($account->name));
$node = content_profile_load($type, $account->uid);
if (!$node) {
$node = array('uid' => $account->uid, 'name' => (isset($account->name) ? $account->name : ''), 'type' => $type, 'language' => '');
}
return drupal_get_form($type .'_node_form', $node);
}
/**
* Implementation of hook_views_api().
*/
function content_profile_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'content_profile') .'/views',
);
}
/**
* Menu callback; content profile settings.
*/
function content_profile_admin_settings(&$form_state, $type) {
$form_state['type'] = $type;
$form['weight'] = array(
'#type' => 'weight',
'#title' => t("Weight"),
'#default_value' => content_profile_get_settings($type, 'weight'),
'#description' => t('The weight of content of this content type where ever they appear - this applies to the input form integration as well to the display integration.'),
'#weight' => 5,
);
$form['display'] = array(
'#type' => 'fieldset',
'#title' => t('Display settings'),
'#description' => t('Customize the display of this content profile.'),
'#collapsible' => TRUE,
);
$form['display']['user_display'] = array(
'#type' => 'radios',
'#title' => t("User page display style"),
'#default_value' => content_profile_get_settings($type, 'user_display'),
'#options' => array(
0 => t("Don't display this content profile on the user account page"),
'link' => t('Display it as link to the profile content'),
'full' => t('Display the full content'),
'teaser' => t("Display the content's teaser"),
),
);
$form['display']['edit_link'] = array(
'#type' => 'checkbox',
'#title' => t("Include an edit link to the display"),
'#default_value' => content_profile_get_settings($type, 'edit_link'),
);
$form['display']['add_link'] = array(
'#type' => 'checkbox',
'#title' => t("Show a link to the content profile creation page, if there is no profile."),
'#default_value' => content_profile_get_settings($type, 'add_link'),
'#description' => t("If selected and the user has no profile of this type yet, a link to add one is shown on the user page."),
);
$form['display']['edit_tab'] = array(
'#type' => 'radios',
'#title' => t("Profile edit tab"),
'#default_value' => content_profile_get_settings($type, 'edit_tab'),
'#options' => array(
0 => t('None'),
'top' => t("Show a tab at the user's page"),
'sub' => t("Show a secondary tab below the user's edit tab"),
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#weight' => 10,
);
return $form;
}
function content_profile_admin_settings_submit($form, &$form_state) {
$settings = content_profile_get_settings($form_state['type']);
foreach (content_profile_settings_info() as $setting => $default) {
if (isset($form_state['values'][$setting])) {
$settings[$setting] = $form_state['values'][$setting];
}
}
content_profile_set_settings($form_state['type'], $settings);
drupal_set_message('Your changes have been saved.');
menu_rebuild();
}
/**
* Determine if a given node is a content_profile.
* @param $type
* The node object or the node's type
*/
function is_content_profile($type) {
if (is_object($type)) {
$type = $type->type;
}
return variable_get('content_profile_use_'. $type, FALSE);
}
/**
* Builds a list of available content types that are marked as content_profiles,
* and returns an array of content profile content types in the specified format.
*
* @param $op
* When set to 'types', content profile content types are returned
* as type objects. When set to 'names', only their type names are returned.
* @param $setting
* If set, only content types that have this setting activated are returned.
* Leave it NULL to get all content profile types.
* @param $value
* The value to compare the given setting too.
*/
function content_profile_get_types($op = 'types', $setting = NULL , $value = TRUE) {
$types = array();
foreach (node_get_types($op) as $type => $info) {
if (is_content_profile($type) && (!isset($setting) || content_profile_get_settings($type, $setting) == $value)) {
$types[$type] = $info;
}
}
return $types;
}
/**
* Implementation of hook_node_type().
* Rename or delete the settings variable if a type changes.
*/
function content_profile_node_type($op, $info) {
switch ($op) {
case 'delete':
variable_del('content_profile_use_'. $info->type);
variable_del('content_profile_'. $info->type);
break;
case 'update':
if (!empty($info->old_type) && $info->old_type != $info->type) {
if (is_content_profile($info->old_type)) {
$settings = variable_get('content_profile_'. $info->old_type, array());
variable_del('content_profile_use_'. $info->old_type);
variable_del('content_profile_'. $info->old_type);
variable_set('content_profile_use_'. $info->type, 1);
variable_set('content_profile_'. $info->type, $settings);
}
}
break;
}
}
/**
* Implementation of hook_form_alter().
*/
function content_profile_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'node_type_form') {
$form['content_profile'] = array(
'#type' => 'fieldset',
'#title' => t('Content Profile'),
'#group' => 'additional_settings',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => 32,
);
$form['content_profile']['content_profile_use'] = array(
'#type' => 'checkbox',
'#title' => t('Use this content type as a content profile for users'),
'#default_value' => variable_get('content_profile_use_'. $form['#node_type']->type, FALSE),
);
}
elseif (isset($form['#node']) && $form['#node']->type .'_node_form' == $form_id && is_content_profile($form['#node'])) {
// Customize the redirect target and buttons of our own node forms.
if (arg(0) == 'user' && is_numeric(arg(1)) && arg(2) == 'edit' || arg(2) == 'profile') {
$form['buttons']['preview']['#access'] = FALSE;
$form['buttons']['delete']['#access'] = FALSE;
$form['#redirect'] = arg(2) == 'profile' ? 'user/'. $form['#node']->uid : $_GET['q'];
}
// Set the author value - note that this works only for admins.
if (!empty($_GET['uid']) && ($uid = intval($_GET['uid'])) && ($user = user_load($uid))) {
$form['author']['name']['#default_value'] = $user->name;
}
}
}
/**
* Implementation of hook_user().
*/
function content_profile_user($op, &$edit, &$account, $category = NULL) {
global $user;
switch ($op) {
case 'categories':
$data = array();
foreach (content_profile_get_types('names', 'edit_tab', 'sub') as $type => $type_name) {
$data[] = array(
'name' => $type,
'title' => drupal_ucfirst($type_name),
'weight' => content_profile_get_settings($type, 'weight') + 1,
);
}
return $data;
case 'view':
$account->content['content_profile'] = content_profile_show_profiles($account->uid);
break;
case 'delete':
// Retrieve all profile nodes (in any language) for this user by issueing an SQL query.
if ($types = content_profile_get_types()) {
$condition = array_fill(0, count($types), "type = '%s'");
$arguments = array_merge(array_keys($types), array($account->uid));
$result = db_query("SELECT * FROM {node} WHERE (". implode(' OR ', $condition) .") AND uid = %d", $arguments);
while ($node = db_fetch_object($result)) {
_content_profile_node_delete($node);
}
}
break;
}
}
/**
* The original node_delete() function uses node_load() to get the $node object.
* Unfortunately, when a hook_user('delete') is called, node_load() doesn't
* work anymore because the user has already been deleted, and node_load()
* still expects the user to exist in the {user} table.
*
* So this is a modified copy of node_delete() that deletes a node without
* calling node_load(), taking the full $node object (as retrieved by a simple
* "SELECT * FROM {node}" query) instead of just the $nid.
*/
function _content_profile_node_delete($node) {
// Copied over from node_load(), so that node_invoke('delete') gets
// the fully extended node object, like modules would expect:
if ($node->nid) {
// Call the node specific callback (if any) and piggy-back the
// results to the node or overwrite some values.
if ($extra = node_invoke($node, 'load')) {
foreach ($extra as $key => $value) {
$node->$key = $value;
}
}
if ($extra = node_invoke_nodeapi($node, 'load')) {
foreach ($extra as $key => $value) {
$node->$key = $value;
}
}
}
// Copied over from node_delete():
db_query('DELETE FROM {node} WHERE nid = %d', $node->nid);
db_query('DELETE FROM {node_revisions} WHERE nid = %d', $node->nid);
// Call the node-specific callback (if any):
node_invoke($node, 'delete');
node_invoke_nodeapi($node, 'delete');
// Clear the cache so an anonymous poster can see the node being deleted.
cache_clear_all();
// Remove this node from the search index if needed.
if (function_exists('search_wipe')) {
search_wipe($node->nid, 'node');
}
watchdog('content', '@type: deleted %title.', array('@type' => $node->type, '%title' => $node->title));
drupal_set_message(t('@type %title has been deleted.', array('@type' => node_get_types('name', $node), '%title' => $node->title)));
}
/**
* Implementation of hook_nodeapi().
*/
function content_profile_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
if ($op == 'prepare' && is_content_profile($node) && !isset($node->nid) && $node->uid && !user_access('administer nodes') && arg(0) != 'admin') {
// Check if this nodetype already exists
if ($nid = content_profile_profile_exists($node, $node->uid)) {
// This node already exists, redirect to edit page
drupal_goto('node/'. $nid .'/edit', 'destination=user/'. $node->uid);
}
}
elseif ($op == 'validate' && is_content_profile($node) && user_access('administer nodes')) {
$form = $a3;
// Only validate if the user-name changed or we add a new node
if (!empty($node->nid) && $form['author']['name']['#default_value'] == $node->name) {
return;
}
//check whether the selected user has already a profile
$uid = db_result(db_query("SELECT uid FROM {users} WHERE name = '%s'", $node->name));
if ($uid && content_profile_profile_exists($node, $uid)) {
form_set_error('name', t('This user already has a content profile of this type. You can only create one profile per user.'));
}
}
elseif ($op == 'prepare translation' && is_content_profile($node->translation_source)) {
// Make sure the translated profile belongs to the same user.
$node->uid = $node->translation_source->uid;
$node->name = $node->translation_source->name;
}
}
/**
* Checks whether a node of this type exists already for the author
*
* @param $node
* The node, which is to be created.
* @param $uid
* The user to check for.
* @return If a node exists, the node id, FALSE else.
*/
function content_profile_profile_exists($node, $uid) {
$query = "SELECT n.nid AS nid FROM {node} n WHERE n.type = '%s' AND n.uid = %d ";
if (module_exists('translation') && translation_supported_type($node->type)) {
$query .= "AND n.language = '%s'";
}
return db_result(db_query($query, $node->type, $uid, $node->language));
}
/**
* Returns the content_profiles' settings.
* @param $type
* The content type to return settings for.
* @param $return
* 'all' or one of the content_profile_available_settings(),
* e.g. user_edit, register or weight.
*/
function content_profile_get_settings($type, $return = 'all') {
$settings = variable_get('content_profile_'. $type, array());
$settings += content_profile_settings_info();
if ($return == 'all') {
return $settings;
}
return $settings[$return];
}
/**
* Saves the content_profile settings of a content type.
*/
function content_profile_set_settings($type, $settings) {
variable_set('content_profile_'. $type, $settings);
}
/**
* Returns an array, which defines the available settings
* and their default value.
*/
function content_profile_settings_info() {
return module_invoke_all('content_profile_settings');
}
/**
* Implementation of hook_content_profile_settings().
*
* Defines content profile settings and their default value.
*/
function content_profile_content_profile_settings() {
return array(
'weight' => 0,
'user_display' => 'full',
'edit_link' => 0,
'edit_tab' => 'sub',
'add_link' => 1,
);
}
/**
* Loads the node, like node_load but makes sure the results are cached.
*
* @param $type
* The content profile's type.
* @param $uid
* The profile owner's user id.
* @param $lang
* Optional. If translation is enabled, the language of the profile to return.
* @param $reset
* Optional. If set, the cache is reset.
*/
function content_profile_load($type, $uid, $lang = '', $reset = NULL) {
static $cache = array();
if (!isset($cache[$type][$uid][$lang]) || $reset) {
$cache[$type][$uid][$lang] = FALSE;
$params = array('type' => $type, 'uid' => $uid);
if ($node = node_load($lang ? $params + array('language' => $lang) : $params, NULL, $reset)) {
$cache[$type][$uid][$lang] = $node->nid;
}
return $node;
}
return !empty($cache[$type][$uid][$lang]) ? node_load($cache[$type][$uid][$lang]) : FALSE;
}
/**
* Implementation of hook_help().
*
* Show node submission guidelines for content profile node forms.
*/
function content_profile_help($path, $arg) {
if (preg_match('/user\/\%\/(profile|edit)\/(.*)/', $path, $matches)) {
foreach (content_profile_get_types('names') as $type => $typename) {
if ($type == $matches[2]) {
$node = content_profile_load($type, $arg[1]);
if ($node) {
return node_help('node/%/edit', array(1 => $node->nid));
}
else {
return node_help('node/add/'. $type, array('node', 'add', $type));
}
}
}
}
}
/**
* Returns an array suitable for use with drupal_render,
* that shows all content_profiles as configured by the admin.
*/
function content_profile_show_profiles($uid) {
global $user;
$content = array();
foreach (content_profile_get_types('names') as $type => $type_name) {
$node = content_profile_load($type, $uid);
if (($style = content_profile_get_settings($type, 'user_display')) && $node && node_access('view', $node)) {
$content['content_profile_'. $type] = array(
'#theme' => ($style == 'link') ? 'content_profile_display_link' : 'content_profile_display_view',
'#edit_link' => content_profile_get_settings($type, 'edit_link'),
'#uid' => $uid,
'#style' => $style,
'#content_type' => $type,
'#weight' => content_profile_get_settings($type, 'weight'),
'#suffix' => '
',
);
// Working around the bug described at http://drupal.org/node/302873
module_load_include('inc', 'content_profile', 'content_profile.theme');
}
elseif (user_access('create '. $type .' content') && content_profile_get_settings($type, 'add_link') && !$node && ($uid == $user->uid || user_access('administer nodes'))) {
$content['content_profile_'. $type] = array(
'#admin' => $uid != $user->uid,
'#theme' => 'content_profile_display_add_link',
'#uid' => $uid,
'#content_type' => $type,
'#weight' => content_profile_get_settings($type, 'weight'),
'#suffix' => '
',
);
}
}
if ($content) {
$content['#prefix'] = '
'; $content['#suffix'] = '
'; } return $content; } /** * Implementation of hook_theme(). */ function content_profile_theme() { $return = array( 'content_profile_display_view' => array( 'template' => 'content_profile-display-view', 'arguments' => array('element' => NULL), 'file' => 'content_profile.theme.inc', ), 'content_profile_display_add_link' => array( 'file' => 'content_profile.theme.inc', ), 'content_profile_display_link' => array( 'file' => 'content_profile.theme.inc', ), 'content_profile_display_tab_view' => array( 'file' => 'content_profile.theme.inc', ), 'content_profile_display_tab_edit' => array( 'file' => 'content_profile.theme.inc', ), ); if (module_exists('pageroute')) { $return['content_profile_pageroute_empty'] = array( 'arguments' => array('type_name' => NULL), 'file' => 'content_profile.pageroute.inc', ); } return $return; } /** * Implementation of hook_theme_registry_alter(). * Adds our own preprocess functions to some templates. Further templates can be set in * $conf['content_profile_extra_templates'] in settings.php. */ function content_profile_theme_registry_alter(&$items) { $templates = array_merge(array( 'author_pane', 'comment', 'node', 'page', 'search_result', 'username', 'user_profile', 'user_signature', ), variable_get('content_profile_extra_templates', array())); foreach ($templates as $key) { if (isset($items[$key])) { $items[$key] += array('preprocess functions' => array()); $items[$key]['preprocess functions'][] = 'content_profile_template_preprocess'; } } } /** * Adds $content_profile variable if we can find a $uid. */ function content_profile_template_preprocess(&$variables, $hook) { // Search the uid foreach (array('account_id', 'uid', 'account', 'node', 'comment', 'user') as $name) { if (isset($variables[$name])) { $uid = is_object($variables[$name]) ? $variables[$name]->uid : $variables[$name]; $variables['content_profile'] = new content_profile_theme_variables($uid); break; } } } /** * Implementation of hook_simpletest(). */ function content_profile_simpletest() { // Scan through content_profile/tests directory for any .test files to tell SimpleTest module. $tests = file_scan_directory(drupal_get_path('module', 'content_profile') .'/tests', '\.test'); return array_keys($tests); } /** * Implementation of hook_pageroute_info() for Pageroute integration. */ function content_profile_pageroute_info() { return array( 'content_profile' => array( 'viewprofile' => 'content_profile.pageroute', 'editprofile' => 'content_profile.pageroute', ) ); }