/[drupal]/contributions/modules/sna/sna.module
ViewVC logotype

Contents of /contributions/modules/sna/sna.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.5 - (show annotations) (download) (as text)
Mon Sep 4 10:13:07 2006 UTC (3 years, 2 months ago) by aronnovak
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-5, DRUPAL-4-7
Changes since 1.4: +11 -0 lines
File MIME type: text/x-php
Realtime network generating capabilities added.
hook_cron() implemented - no dumb extra files that have to be added to crontab
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Add social networking functionality to the user interface
7 *
8 * Main things: degree count, searching routes and measure separation
9 * computing clustering coefficient, searching groups in network
10 */
11
12 /**
13 * All the sna-related function is in routes.php
14 */
15 require 'routes.php';
16
17 /**
18 * Implementation of hook_perm().
19 */
20 function sna_perm() {
21 return array('access sna', 'browse sna map', 'realtime shortest route searching in sna', 'browse sna groups');
22 }
23
24 /**
25 * Get all the operations of the module
26 *
27 * @return array The operations
28 */
29 function sna_get_operations() {
30 $op = array(
31 array(
32 'title' => t('Searching routes'),
33 'desc' => t('Searching shortest route with Dijksta-algorithm or searching minimal step route with breadth-first search.'),
34 'path' => 'sna/routes',
35 'access' => 'access sna'),
36 array(
37 'title' => t('Map of the network'),
38 'desc' => t('A JAVA applet shows the network and you can explore within it.'),
39 'path' => 'sna/map',
40 'access' => 'browse sna map'),
41 array(
42 'title' => t('Distribution of edges'),
43 'desc' => t('Many basic features of the network topology can be read from the vertex
44 degree distribution. A vertex degree is the number corresponding to
45 number of vertices to which the vertex is directly connected. Small-world
46 network gives also the exponential dependence on vertex degree. Networks,
47 where the distribution of vertex degree is of power-law type are called a scale-free networks.'),
48 'path' => 'sna/dist',
49 'access' => 'access sna'),
50 array(
51 'title' => t('Explore groups'),
52 'desc' => t('Here you can see the strongly connected component of the social network.
53 A directed graph (the network is represented by a directed graph) is called strongly
54 connected if for every pair of vertices u and v there is a path from u to v and a path from v to u.'),
55 'path' => 'sna/groups',
56 'access' => 'browse sna groups'));
57 return $op;
58 }
59
60 /**
61 * Implementation of hook_help()
62 */
63 function sna_help($section = '') {
64 switch ($section) {
65 case "admin/modules#description":
66 $output .= t("Social network module");
67 break;
68 }
69 return $output;
70 }
71
72 /**
73 * Implementation of hook_user()
74 */
75 function sna_user($op, &$edit, &$account, $category = NULL) {
76 global $user;
77 if ($op == 'view') {
78 if (!user_access('access sna')) {
79 return array();
80 }
81 $edges = array();
82 get_graph($edges);
83 $output[] = array('title' => t("Number of direct connections"), 'value' => vertex_degree($edges, $account->uid));
84 $output[] = array('title' => t("Clustering coefficient"), 'value' => clustering_coefficient($edges, $account->uid));
85 if ($user->uid !== $account->uid) {
86 // If it's not our own user page
87 $possibilities[] = l(t('Shortest route from ') . $account->name . t(" to ") . $user->name, 'sna/a_to_b/' . $account->uid .'/' . $user->uid . '/0');
88 $possibilities[] = l(t('Shortest route from ') . $user->name . t(" to ") . $account->name, 'sna/a_to_b/' . $user->uid . '/' . $account->uid . '/0');
89 $possibilities[] = l(t('Minimal step route from ') . $account->name . t(" to ") . $user->name, 'sna/a_to_b/' . $account->uid .'/' . $user->uid . '/1');
90 $possibilities[] = l(t('Minimal step route from ') . $user->name . t(" to ") . $account->name, 'sna/a_to_b/' . $user->uid . '/' . $account->uid . '/1');
91 $output[] = array('title' => t("Search route"), 'value' => theme_item_list($possibilities));
92 }
93 if (user_access('browse sna map')) {
94 $draws[] = l(t('Draw user\'s tree of minimal routes'), 'sna/tree_draw/' . $account->uid . '/'. ROUTE_SHORTEST);
95 $draws[] = l(t('Draw user\'s tree of breadth-first search'), 'sna/tree_draw/' . $account->uid . '/' . ROUTE_MIN_STEP);
96 $output[] = array('title' => t('Visualization'), 'value' => theme_item_list($draws));
97 }
98 /* Collect all the direct connections into a user list */
99 if (isset($edges[$account->uid])) {
100 foreach (array_keys($edges[$account->uid]) as $direct_conn) {
101 $group[] = user_load(array('uid' => $direct_conn));
102 }
103 $output[] = array('title' => t('Direct connections'),
104 'value' => theme_box(t('From this user'), theme('user_list', $group)));
105 }
106 return array(t("Social network informations") => $output);
107 }
108 }
109
110 /**
111 * Implementation of hook_block().
112 */
113 function sna_block($op = 'list', $delta = 0, $edit = array()) {
114 global $user;
115 if ($op == 'list') {
116 $blocks[0]['info'] = t('Most linked users');
117 $blocks[1]['info'] = t('Average step separation');
118 return $blocks;
119 }
120 else if ($op == 'view') {
121 $edges = array();
122 get_graph($edges);
123 $block = array();
124
125 switch ($delta) {
126 case 0:
127 $max_users = variable_get('user_block_max_list_count', 10);
128 $top_users = sort_by_popularity($edges, $max_users);
129 $num_top_users = count($top_users);
130 $how_many_list = $num_top_users > $max_users ? $max_users : $num_top_users ;
131 for ($i = 0; $i < $how_many_list; $i++) {
132 $items[] = user_load(array('uid' => $top_users[$i][1]));
133 }
134
135 $output .= theme('user_list', $items);
136 $block['subject'] = t('Most linked users');
137 $block['content'] = $output;
138 return $block;
139
140 case 1:
141 $block['subject'] = t('Average step separation');
142 $block['content'] = round(average_step_separation($edges), 3);
143 return $block;
144 }
145 }
146 }
147
148 /**
149 * Create form of sna-related commands.
150 * Commands:
151 * - BFS
152 * - Dijkstra
153 *
154 */
155 function sna_routes() {
156 // Generate form for searching route
157 $form['sna_a_to_b'] = array(
158 '#type' => 'fieldset',
159 '#title' => t('Searching best route between users'),
160 '#tree' => TRUE);
161 $form['sna_a_to_b']['from'] = array(
162 '#type' => 'select',
163 '#title' => t('From'),
164 '#options' => get_all_vertices_for_forms());
165 $form['sna_a_to_b']['to'] = array(
166 '#type' => 'select',
167 '#title' => t('To'),
168 '#options' => get_all_vertices_for_forms());
169 $form['sna_a_to_b']['min_or_step'] = array(
170 '#type' => 'radios',
171 '#title' => t('Type of route searching'),
172 '#options' => array(ROUTE_SHORTEST => t('Shortest route'),
173 ROUTE_MIN_STEP => t('Minimal step route')));
174 $form['sna_a_to_b']['submit'] = array('#type' => 'submit', '#value' => t('Search'));
175 return drupal_get_form('sna_a_to_b', $form);
176 }
177
178 /**
179 * Create the map-viewer applet
180 * Currently use smallworld (http://www.cs.ubc.ca/~sfingram/cs533C/small_world.html)
181 *
182 * @param string $params The applet's parameter - list of edges
183 * @param string $title This string be written in the applet window
184 *
185 */
186 function sna_applet($params, $title) {
187 $output = '<applet code = "app/SmallWorldApplet"
188 archive = "'. APPLET_PATH .'prefuse.jar,
189 '. APPLET_PATH .'colt.jar,
190 '. APPLET_PATH .'smallworld.jar" height = "400" width = "300">';
191 $output .= $params;
192 $output .= '<param name = "title" value = "'. $title .'"></applet>';
193 return $output;
194 }
195
196 /**
197 * Show the entire social network map with the help of a java applet
198 * Can be very resource-consuming. Not suggested at large sites with many users!
199 *
200 */
201 function sna_map() {
202 get_graph($edges);
203 return sna_applet(show_map($edges), t('The social network map of this site'));
204 }
205
206 /**
207 * Draw the distribution of edges in an SVG file.
208 *
209 */
210 function sna_dist() {
211 return '<object type="image/svg+xml" name="edge_dist" data="' . base_path() . '?q=sna/svg_distribution_of_edges" width="' . PIC_WIDTH .
212 '" height="' . PIC_HEIGHT . '">' . t('SVG support is needed') . '</object>';
213 }
214
215 /**
216 * Display the strongly connected components of the social graph
217 *
218 * @return string The formatted output of grups
219 */
220 function sna_groups() {
221 get_graph($edges);
222 $groups = search_groups($edges);
223 $num_of_groups = count($groups);
224 for ($act_group = 0; $act_group < $num_of_groups; $act_group++) {
225 $users_in_this_group = count($groups[$act_group]);
226 for ($act_user = 0; $act_user < $users_in_this_group; $act_user++) {
227 $group[] = user_load(array('uid' => $groups[$act_group][$act_user]));
228 }
229 $output .= theme_box(t('Group ' . ($act_group + 1)), theme('user_list', $group));
230 $group = array();
231 }
232 if (empty($output)) {
233 $output = t('No groups in the network yet.');
234 }
235 drupal_set_message(t('A directed graph is called strongly connected if for every pair
236 of vertices u and v there is a path from u to v and a path from
237 v to u. The strongly connected components (SCC) of a directed graph
238 are its maximal strongly connected subgraphs. These form a partition
239 of the graph. (source: Wikipedia)'));
240 return $output;
241 }
242
243 /**
244 * Handle the form of the sna_operations page
245 *
246 */
247 function sna_a_to_b_submit($form_id, $form_values) {
248 $from = $form_values[$form_id]['from'];
249 $to = $form_values[$form_id]['to'];
250 $type = $form_values[$form_id]['min_or_step'];
251 drupal_set_message(sna_a_to_b_common($from, $to, $type));
252 }
253
254 /**
255 * Display a route between two users
256 *
257 * @param integer $from The from drupal user id
258 * @param integer $to The to drupal user id
259 * @param integer $type If 0 - Dijkstra, if 1 - breadth-first search
260 *
261 */
262 function sna_a_to_b_show($from = FALSE, $to = FALSE, $type = FALSE) {
263 return sna_a_to_b_common($from, $to, $type);
264 }
265
266 /**
267 * Create the table of the minimal route
268 *
269 * @param integer $from User id
270 * @param integer $to User id
271 * @param integer $type ROUTE_MIN_STEP or ROUTE_SHORTEST
272 * @return string The themed table
273 */
274 function sna_a_to_b_common($from, $to, $type) {
275 if (!is_numeric($from) || !is_numeric($to) || !($type == ROUTE_MIN_STEP || $type == ROUTE_SHORTEST)) {
276 drupal_access_denied();
277 return '';
278 }
279 get_graph($edges);
280 if ($type == ROUTE_SHORTEST) {
281 if (user_access('realtime shortest route searching in sna') || is_in_cache($from) !== FALSE) {
282 $min_route = a_to_b($edges, $from, $to);
283 }
284 else {
285 return drupal_set_message(t('Realtime shortest route searching is disabled'), 'error');
286 }
287 }
288 else {
289 $min_route = a_to_b($edges, $from, $to, TRUE);
290 }
291 if ($from == $to) {
292 return t('The destination and the source is the same.');
293 }
294 else if (count($min_route) == 0 || $min_route === FALSE) {
295 return t('No route from ') . get_real_name($from) . t(' to ') . get_real_name($to);
296 }
297 else {
298 $index = 0;
299 $route[] = array(t("User"), t(($type == 0 ? "Distance " : "Step ") . "from the source"));
300 for ($i = end($min_route); is_array($i); $i = prev($min_route)) {
301 $route[] = array(get_real_name($i['n']), $i['d']);
302 }
303 return theme_table(array(), $route, array());
304 }
305 return '';
306 }
307
308 /**
309 * Display a tree originating from <var>$uid</var>
310 *
311 * @param integer $uid Drupal uid
312 * @param integer $type If 0 - Dijkstra, if 1 - breadth-first search
313 *
314 */
315 function sna_tree_draw($uid, $type) {
316 get_graph($edges);
317 if ($type == ROUTE_MIN_STEP) { // Dijkstra search (slow!)
318 $tree = a_to_any($edges, $uid);
319 }
320 else { // Breadth-first search
321 $tree = breadth_first_walk($edges, $uid);
322 }
323 return sna_applet(show_tree($tree), t('The tree of user ') . get_real_name($uid));
324 }
325
326 /**
327 * Create the page of the distribution of edges. This is an svg document.
328 *
329 */
330 function sna_svg_distribution() {
331 get_graph($edges);
332 header('Content-type: image/svg+xml');
333 print visualize_distribution(distribution_of_degree($edges));
334 }
335
336 /**
337 * Create a summary of possible commands
338 *
339 * @return string The commands with descriptions
340 */
341 function sna_page() {
342 $ops = sna_get_operations();
343 $num_ops = count($ops);
344 for ($i = 0; $i < $num_ops; $i++) {
345 if (user_access($ops[$i]['access'])) {
346 $output .= '<dt>' . l($ops[$i]['title'], $ops[$i]['path']) . '</dt><dd>' . $ops[$i]['desc'] . '</dd>';
347 }
348 else {
349 $output .= '<dt>' . $ops[$i]['title'] . '</dt><dd>' . t('function is disabled') . '</dd>';
350 }
351 }
352 return $output;
353 }
354
355 /**
356 * Implementation of hook_menu().
357 *
358 * @return array Menu elements
359 */
360 function sna_menu($may_cache) {
361 $items[] = array('path' => 'sna', 'title' => t('Social networking'), 'callback' => 'sna_page',
362 'access' => user_access('access sna'), 'type' => MENU_ITEM_GROUPING);
363 // Make submenus
364 $ops = sna_get_operations();
365 $num_ops = count($ops);
366 for ($i = 0; $i < $num_ops; $i++) {
367 $items[] = array('path' => $ops[$i]['path'],
368 'title' => $ops[$i]['title'],
369 'callback' => str_replace('/', '_', $ops[$i]['path']) ,
370 'access' => user_access($ops[$i]['access']));
371 }
372 $items[] = array('path' => 'sna/a_to_b',
373 'title' => t('Search routes'),
374 'callback' => 'sna_a_to_b_show',
375 'access' => user_access('access sna'),
376 'type' => MENU_CALLBACK);
377 $items[] = array('path' => 'sna/tree_draw',
378 'title' => t('Drawing tree'),
379 'callback' => 'sna_tree_draw',
380 'access' => TRUE,
381 'type' => MENU_CALLBACK);
382 $items[] = array('path' => 'sna/svg_distribution_of_edges',
383 'title' => t(''),
384 'callback' => 'sna_svg_distribution',
385 'access' => TRUE,
386 'type' => MENU_CALLBACK);
387 return $items;
388 }
389
390 /**
391 * Implementation of hook_settings()
392 *
393 * @return array The form of settings
394 */
395 function sna_settings() {
396 /*if (!user_access("access administration pages")) {
397 return message_access();
398 }*/
399 if (function_exists('dba_handlers') && SNA_CACHE_ENABLED) {
400 if (array_search(DBA_HANDLER, array_keys(dba_handlers())) === FALSE) {
401 drupal_set_message(t('The proper dba_handler support is missing.'), 'error');
402 drupal_set_message(t('You have to choose a valid dba_handler in common.php'), 'status');
403 }
404 }
405 drupal_set_message(t('The network database is updated: ') . date('F d Y H:i:s', filemtime(DATA_PATH)), 'status');
406
407 $form['sna_data_source'] = array('#title' => t('Data source of the network'),
408 '#type' => 'select',
409 '#options' => array('nodes' => 'Comments and replies', 'buddy' => 'Buddymodule', 'stats' => 'Viewing other\'s user page'),
410 '#default_value' => variable_get('sna_data_source', 'nodes'),
411 '#description' => t('The relations between users can be gathered from various source.
412 For example if you choose the Comments and replies option the
413 comments will be the base of the connection. If someone reply to a node
414 or another comment it results an edge in the network.'));
415 if (SNA_CACHE_ENABLED) {
416 $form['sna_size_cache'] = array('#title' => t('Number of cached shortest routes'),
417 '#type' => 'textfield',
418 '#default_value' => variable_get('sna_size_cache', 10),
419 '#description' => t('The Dijkstra algorithm will be executed on the top-n users by
420 sna_cron script so when the visitors want to search shortest route
421 it can be served from cache fast.'));
422 }
423 $form['sna_data_path'] = array('#title' => t('Data files path which is writeable by the web server'),
424 '#type' => 'textfield',
425 '#default_value' => variable_get('sna_data_path', FILES_PATH),
426 '#description' => t('Trailing / is needed.'));
427 $form['sna_pic_size'] = array('#title' => t('Size of SVG picture in pixel'),
428 '#type' => 'textfield',
429 '#default_value' => variable_get('sna_pic_size', PIC_WIDTH),
430 '#description' => t('The distribution of edges picture\'s size'));
431 $form['sna_limit'] = array('#title' => t('Ignore edges longer than'),
432 '#type' => 'textfield',
433 '#default_value' => variable_get('sna_limit', 0),
434 '#description' => t('Zero means no limit. You have to raise this number if the graph
435 function become too slow.'));
436
437 $form['sna_realtime_network'] = array('#title' => t('Realtime network building'),
438 '#type' => 'checkbox',
439 '#default_value' => variable_get('sna_realtime_network', 0),
440 '#description' => t('Turning on realtime network generation. It means that the
441 social network is rebuilt at every page request.
442 At large sites it can be extremly slow!.'));
443 return $form;
444 }
445
446 function sna_cron() {
447 include 'explore.php';
448 }
449
450 ?>

  ViewVC Help
Powered by ViewVC 1.1.2