diff -ru webfm-6.x-2.9-alpha2/js/webfm.js webfm/js/webfm.js --- webfm-6.x-2.9-alpha2/js/webfm.js 2008-04-08 08:28:14.000000000 +0200 +++ webfm/js/webfm.js 2008-08-16 11:32:00.000000000 +0200 @@ -92,6 +92,8 @@ Webfm.menu_msg["ren"] = "Rename File"; Webfm.menu_msg["meta"] = "File meta data"; Webfm.menu_msg["att"] = "Attach to Node"; +Webfm.menu_msg["attdir"] = "Attach all Files in Folder to Node"; +Webfm.menu_msg["unzip"] = "Extract to here"; Webfm.menu_msg["det"] = "Detach from Node"; Webfm.menu_msg["dwnld"] = "Download as file"; Webfm.menu_msg["view"] = "View file"; @@ -297,6 +299,7 @@ //Add attach-list menu and attach option to listing menu try { Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg["att"], Webfm.menuAttach, Webfm.menuFidVal)); + Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg["attdir"], Webfm.menuAttachDir, Webfm.menuFidVal)); Webfm.menuHT.put('det', new Webfm.menuElement(Webfm.menu_msg["meta"], Webfm.menuGetMeta, '')); Webfm.menuHT.put('det', new Webfm.menuElement(Webfm.menu_msg["det"], Webfm.menuDetach, '')); } catch(err) { @@ -346,12 +349,16 @@ //global to allow external functions to push new menu elements into the menu array Webfm.menuHT = new Webfm.hht(); try { - Webfm.menuHT.put('root', new Webfm.menuElement(Webfm.menu_msg["mkdir"], Webfm.menuMkdir, Webfm.menuAdmin)); + //Webfm.menuHT.put('root', new Webfm.menuElement(Webfm.menu_msg["mkdir"], Webfm.menuMkdir, Webfm.menuAdmin)); + Webfm.menuHT.put('root', new Webfm.menuElement(Webfm.menu_msg["mkdir"], Webfm.menuMkdir, '')); Webfm.menuHT.put('root', new Webfm.menuElement(Webfm.menu_msg["search"], Webfm.menuSearch, '')); - Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["mkdir"], Webfm.menuMkdir, Webfm.menuAdmin)); - Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["rmdir"], Webfm.menuRemove, Webfm.menuAdmin)); - Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["rendir"], Webfm.menuRename, Webfm.menuAdmin)); + //Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["mkdir"], Webfm.menuMkdir, Webfm.menuAdmin)); + //Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["rmdir"], Webfm.menuRemove, Webfm.menuAdmin)); + //Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["rendir"], Webfm.menuRename, Webfm.menuAdmin)); + Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["mkdir"], Webfm.menuMkdir, '')); + Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["rmdir"], Webfm.menuRemove, '')); + Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["rendir"], Webfm.menuRename, '')); Webfm.menuHT.put('dir', new Webfm.menuElement(Webfm.menu_msg["search"], Webfm.menuSearch, '')); Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg["rm"], Webfm.menuRemove, Webfm.menuFileUid)); @@ -362,7 +369,8 @@ Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg["enum"], Webfm.menuInsert, Webfm.menuAdminNoFidVal)); Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg["denum"], Webfm.menuDbRem, Webfm.menuAdminFidVal)); Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg["perm"], Webfm.menuGetPerm, Webfm.menuFilePerm)); - Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg["clip"], Webfm.menuPutLinkInClipboard, Webfm.menuFidVal)) + Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg["clip"], Webfm.menuPutLinkInClipboard, Webfm.menuFidVal)); + Webfm.menuHT.put('file', new Webfm.menuElement(Webfm.menu_msg["unzip"], Webfm.menuUnzip, Webfm.menuZipVal)); } catch(err) { alert("Menu Create err\n" + err); } @@ -1793,6 +1819,25 @@ } } +// Attach all files in directory to node +Webfm.menuAttachDir = function(obj) { + files = Webfm.dirListObj.content.files; + for(item in files) { + obj.clickObj.title = files[item]['id']; + Webfm.menuAttach(obj); + } +} + +// Unzip ZIP-Archive +Webfm.menuUnzip = function(obj) { + var url = Webfm.ajaxUrl(); + Webfm.progressObj.show(Webfm.js_msg["work"], "blue"); + var path = obj.element.title; + path = path.substring(0, path.lastIndexOf("/")); + var postObj = { action:encodeURIComponent("unzip"), param0:encodeURIComponent(obj.clickObj.title) }; + Webfm.HTTPPost(url, Webfm.dbrem_callback, path, postObj); +} + Webfm.menuDetach = function(obj) { Webfm.alrtObj.msg(); var path = obj.element.title; @@ -1957,6 +2002,13 @@ return false; } +Webfm.menuZipVal = function(obj) { + if(obj.fmime == 'application/zip') + return true; + //return false; + return true; +} + Webfm.confirm = function(text) { var agree = confirm(text); return agree ? true : false; @@ -2461,9 +2513,21 @@ Webfm.attach.prototype.fetch = function() { var url = Webfm.ajaxUrl(); // action attribute of node-edit form contains the node number - var node_url = Webfm.$('node-form').action; + var node_url; + if(Webfm.$('node-form')) { + node_url = Webfm.$('node-form').action; + } + if(Webfm.$('comment-form')) { + node_url = Webfm.$('comment-form').action; + } Webfm.progressObj.show(Webfm.js_msg["work"], "blue"); var postObj = { action:encodeURIComponent("attach"), param0:encodeURIComponent(node_url) }; + // If we are in a preview/validate, the fids are still stored in the form. + // Pass them to webfm_ajax to reload them. + var fids = Webfm.$(Webfm.attachFormInput).value; + if (fids.length != 0) { + postObj.param1 = encodeURIComponent(fids); + } Webfm.HTTPPost(url, this.callback, this, postObj); } @@ -2476,9 +2540,20 @@ if(result.data.length) { Webfm.admin = result.admin; var elInput = Webfm.$(Webfm.attachFormInput); + var attach_arr = []; + attach_arr = Webfm.$(Webfm.attachFormInput).value.split(','); + Webfm.current = null; for(var i = 0; i < result.data.length; i++) { var filerow = new Webfm.filerow(obj.body, result.data[i], 'attach', '', true, Webfm.menuHT.get('det'), obj.eventListeners); - elInput.setAttribute('value', (elInput.getAttribute('value')?elInput.getAttribute('value')+',':'') + result.data[i].id); + // Don't add if it already exists. + // Note that values are kept in the form for preview/failed validate. + for (var j = 0; j < attach_arr.length; j++) { + if (result.data[i].id == attach_arr[j]) + break; + } + if (j == attach_arr.length) { + elInput.setAttribute('value', (elInput.getAttribute('value')?elInput.getAttribute('value')+',':'') + result.data[i].id); + } } } } else @@ -3505,6 +3580,7 @@ var toSend = ''; if (typeof object == 'object') { xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xmlHttp.setRequestHeader("X-Requested-With", "XMLHttpRequest"); for (var i in object) { toSend += (toSend ? '&' : '') + i + '=' + encodeURIComponent(object[i]); } diff -ru webfm-6.x-2.9-alpha2/webfm.install webfm/webfm.install --- webfm-6.x-2.9-alpha2/webfm.install 2008-04-08 08:28:14.000000000 +0200 +++ webfm/webfm.install 2008-07-27 13:06:00.000000000 +0200 @@ -76,12 +76,27 @@ 'nid' => array('type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'default' => 0), 'fid' => array('type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'default' => 0), 'weight' => array('type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'default' => 0), + 'cid' => array('type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'default' => 0), ), - 'primary key' => array('nid', 'fid'), + 'primary key' => array('nid', 'fid', 'cid' ), ); return $schema; } +/** + * Add column for comment id in webfm_attach table. + */ +function webfm_update_1() { + $ret = array(); + // Add the new colum to store a comment id. + db_add_field($ret, 'webfm_attach', 'cid', array('type' => 'int', 'not null' => TRUE, 'default' => '0')); + + // Make it a primary key. + db_drop_primary_key($ret, 'webfm_attach'); + db_add_primary_key($ret, 'webfm_attach', array('nid', 'cid', 'fid')); + return $ret; +} + // switch ($GLOBALS['db_type']) { // case 'mysql': diff -ru webfm-6.x-2.9-alpha2/webfm.module webfm/webfm.module --- webfm-6.x-2.9-alpha2/webfm.module 2008-04-23 17:50:06.000000000 +0200 +++ webfm/webfm.module 2008-08-16 11:27:00.000000000 +0200 @@ -156,6 +156,7 @@ function webfm_admin_settings_validate($form, $form_state) { $valid_webfm_root = FALSE; $webfm_root_dir_name = $form_state['values']['webfm_root_dir']; + if(!empty($webfm_root_dir_name)) { if(!preg_match('/^[0-9a-zA-Z]/', $webfm_root_dir_name)) { form_set_error('webfm_root_dir'. $rid, t('The leading character of the webfm root directory name must be alphanumeric.')); @@ -167,6 +168,32 @@ } } +/////////////////////////////////////////////////////////////////////////////// + + $valid_user_root = FALSE; + // Allow tokenized paths. + if (function_exists('token_replace')) { + // Validate with current user... + global $user; + $webfm_user_dir_name = token_replace($form_state['values']['webfm_user_dir'], 'user', $user); + } + else { + $webfm_user_dir_name = $form_state['values']['webfm_user_dir']; + } + + if(!empty($webfm_user_dir_name)) { + if(!preg_match('/^[0-9a-zA-Z]/', $webfm_user_dir_name)) { + form_set_error('webfm_user_dir'. $rid, t('The leading character of the webfm user directory name must be alphanumeric.')); + } else if(preg_match('[\.]', $webfm_user_dir_name)) { + form_set_error('webfm_user_dir'. $rid, t('The webfm user directory name is not valid.')); + } else { + $webfm_user_dir = $webfm_root_dir ."/".$webfm_user_dir_name; + $valid_webfm_user = file_check_directory($webfm_user_dir, FILE_CREATE_DIRECTORY, 'webfm_user_dir'); + } + } + +/////////////////////////////////////////////////////////////////////////////// + if(($form_state['values']['webfm_max_resolution'] != '0')) { if(!preg_match('/^[0-9]+[xX][0-9]+$/', $form_state['values']['webfm_max_resolution'])) { form_set_error('webfm_max_resolution', t('The maximum allowed image size expressed as WIDTHxHEIGHT (e.g. 640x480). Set to 0 for no restriction.')); @@ -180,7 +207,7 @@ $uploadsize = $form_state['values']['webfm_uploadsize_'. $rid]; $usersize = $form_state['values']['webfm_usersize_'. $rid]; $role_root_dir_name = $form_state['values']['root_dir_'.$rid]; - + if(!empty($role_root_dir_name)) { if($valid_webfm_root) { if(!preg_match('/^[0-9a-zA-Z]/', $role_root_dir_name)) { @@ -232,6 +259,25 @@
exist for this setting to validate (ie: path/to).') ); + + $form['webfm_user_dir'] = + array('#type' => 'textfield', + '#title' => t('WebFM user directory'), + '#default_value' => variable_get('webfm_user_dir', ''), + '#maxlength' => '100', + '#size' => '70', + '#description' => t('Root directory for users. +
This path is relative to "WebFM root directory" path. +
If this directory path is compound (ie: path/to/user) then the path must already +
exist for this setting to validate (ie: path/to).') + + ); + + if (function_exists('token_replace')) { + $form['webfm_user_dir']['#description'] .= ' '. t('You can use the following tokens:'); + $form['webfm_user_dir']['#suffix'] = theme('token_help', 'user'); + } + $form['webfm_icon_dir'] = array('#type' => 'textfield', '#title' => t('Icon directory'), @@ -339,7 +385,9 @@ '#default_value' => variable_get("webfm_extensions_".$rid, "jpg jpeg gif png txt html htm doc xls pdf ppt pps"), '#maxlength' => 255, '#description' => t('Extensions that users in this role can upload. Separate extensions with a space -
and do not include the leading dot.') +
and do not include the leading dot. +
Enter * to accept all extensions.'), + ); $form["settings_role_".$rid]["webfm_uploadsize_".$rid] = @@ -542,9 +590,96 @@ } /** + * Implementation of hook_comment(). + * + * Similar to webfm_nodeapi, but for comments instead of nodes. + * Note: Enabling webfm in the edit form is done in form_alter. Keeping the + * attachments while preview or a failing form_validate is done by form_alter + * and webfm_ajax. + * webfm_comment is only needed to save the attachments to the table and + * for viewing and previewing comments. + * We don't need to initialize js in 'validate' here because a failing form + * validation re-initializes the whole node, including js. + */ +function webfm_comment(&$comment, $op) { + if (is_object($comment)) { + $cid = $comment->cid; + $nid = $comment->nid; + } + else { + $cid = $comment['cid']; + $nid = $comment['nid']; + } + + # We need the parent node for checking its permission to view attachments etc. + $node = node_load($nid); + + switch ($op) { + case 'view': + if (variable_get("wfm_attach_$node->type", 1) == 1 && + user_access('view webfm attachments') && + variable_get('webfm_attach_body', '')) { + // If we preview a comment, $comment->preview is defined because the preview button + // for comments is added with $form['preview']. Thus we know that $_POST['attachlist'] + // is ours. If we are previewing another comment and just "view" this one, + // the preview flag is not set and $_POST['attachlist'] belongs to someone else. + // This happens when previewing or editing a comment and the node and/or + // other comments might be shown, too. + // If we preview without 'acces webfm' permissions, we fetch from database, + // cf. nodeapi below. + if ($comment->preview && user_access('access webfm')) { + if ($_POST['attachlist']) { + $show_files = webfm_get_temp_attachments($_POST['attachlist']); + } + } + else { + // Normal view. Try to load attachments. There is no 'load' op for hook_comment. + if (!isset($comment->webfm_files)) { + $comment->webfm_files = webfm_get_attachments($cid, 'cid'); + } + if (is_array($comment->webfm_files) && count($comment->webfm_files)) { + $show_files = $comment->webfm_files; + } + } + if ($show_files) { + $comment->comment .= theme('webfm_attachments', $show_files); + drupal_add_css(drupal_get_path('module', 'webfm').'/css/webfm.css'); + } + } + break; + + case 'insert': + if ($_POST['attachlist']) { + $files = explode(',', $_POST['attachlist']); + $i = 0; + foreach ($files as $fid) { + if ($fid) + // weight argument determined by position in csv + webfm_dbinsert_attach(0, $fid, $i++, $cid); + } + } + break; + + case 'update': + // If the user cannot access webfm, $_POST['attachlist'] is always empty + // and therefore will delete existing attachments from the node. + if (user_access('access webfm')) { + $files = explode(',', $_POST['attachlist']); + webfm_dbupdate_attach(0, $files, $cid); + } + break; + + case 'delete': + webfm_dbdelete_attachments($cid, 'cid'); + break; + } +} + +/** * Implementation of hook_nodeapi(). */ function webfm_nodeapi(&$node, $op, $teaser) { + global $user; switch ($op) { case 'load': if ((variable_get("wfm_attach_$node->type", 1) == 1) && @@ -555,19 +690,61 @@ break; case 'view': - // Add the attachments list to node body if configured to appear in body - if (is_array($node->webfm_files) && variable_get('webfm_attach_body', '')) { - if (count($node->webfm_files) && !$teaser) { + // Add the attachments list to node body if configured to appear in body. + if (variable_get('webfm_attach_body', '')) { + // We could be viewing or previewing this node. + // Loading a node defines $node->webfm_files, possibly as empty list, + // but loading an edit form unsets $node->webfm_files again. Thus, if + // $node->webfm is not set, we are previewing this node. + // Two cases then: + // 1) The user has no webfm access rights, so he can't change anything + // and $_POST['attachlist'] is empty. Simulate the 'load' operation + // to get the files from the database to show them in the preview. + // 2) If user has webfm access, all attachments (saved/unsaved) + // are in $_POST['attachlist']. But $_POST['attachlist'] is also set if + // we preview e.g. a comment where this node is shown, too, but then + // the attachlist is not ours. So we check for webfm_files *before* + // checking for $_POST to get around this. + if (!user_access('access webfm') && (variable_get("wfm_attach_$node->type", 1) == 1) + && user_access('view webfm attachments')) { + $node->webfm_files = webfm_get_attachments($node->nid); + } + if (is_array($node->webfm_files)) { + if (count($node->webfm_files) && !$teaser) { + $show_files = $node->webfm_files; + } + } + // We must check for view permissions in a preview, but wfm_attach_$node->type + // is true if we already have attachments here. + elseif ($_POST['attachlist'] && user_access('view webfm attachments')) { + $show_files = webfm_get_temp_attachments($_POST['attachlist']); + } + if ($show_files) { $node->content['webfm_attachments'] = array( - '#value' => theme('webfm_attachments', $node->webfm_files), + '#value' => theme('webfm_attachments', $show_files), '#weight' => 10, - ); + ); drupal_add_css(drupal_get_path('module', 'webfm').'/css/webfm.css'); } } break; + case 'validate': + // When form_validate fails for preview or save, we must reinitialize + // javascript, otherwise webfm doesn't work anymore. + $modulepath = drupal_get_path('module', 'webfm'); + drupal_add_js($modulepath .'/js/webfm.js'); + drupal_add_css($modulepath .'/css/webfm.css'); + if (is_null($inline_js)) { + $clean_url = variable_get('clean_url', 0); + $clean = (($clean_url == 0) || ($clean_url == '0')) ? FALSE : TRUE; + $inline_js = webfm_inline_js($base_url, $clean, $user->uid); + } + break; + case 'insert': + // We saved the attachment list for preview. Remove before saving. + unset($node->attachlist); if($_POST['attachlist']) { $files = explode(',', $_POST['attachlist']); $i = 0; @@ -580,8 +757,26 @@ break; case 'update': - $files = explode(',', $_POST['attachlist']); - webfm_dbupdate_attach($node->nid, $files); + // We saved the attachment list for preview. Remove before saving. + unset($node->attachlist); + // If the user cannot access webfm, $_POST['attachlist'] is always empty + // and therefore will delete existing attachments from the node. + if(user_access('access webfm')) { + /* + $orderfiles = webfm_get_attachments($node->nid); + usort($orderfiles, function cmp($a, $b){ return strcmp($a->n, $b->n);}); + $files = array(); + foreach($orderfiles as $file) { + $files[] = $file->id; + } + */ + $files = explode(',', $_POST['attachlist']); + webfm_dbupdate_attach($node->nid, $files); + } + break; + + case 'delete': + webfm_dbdelete_attachments($node->nid); break; } } @@ -607,12 +802,17 @@ ); } - if (isset($form['type'])) { - $node = $form['#node']; - if ($access && - $form['type']['#value'] .'_node_form' == $form_id && - variable_get('webfm_attach_'.$node->type, 0)) { - + if (isset($form['type']) || $form_id == 'comment_form') { + // For a comment form, the webfm permissions are inherited from the comments node. + if ($form_id == 'comment_form') { + $node = node_load($form['nid']['#value']); + $formcheck = TRUE; + } + else { + $node = $form['#node']; + $formcheck = ($form['type']['#value'] .'_node_form' == $form_id); + } + if ($access && $formcheck && variable_get('webfm_attach_'. $node->type, 0)) { $modulepath = drupal_get_path('module', 'webfm'); drupal_add_js($modulepath.'/js/webfm.js'); drupal_add_css($modulepath.'/css/webfm.css'); @@ -703,11 +903,24 @@ function webfm_attach_attached_form($node) { $form['#theme'] = 'webfm_attach_attached_form'; - // This form input (id = edit-attachlist) will hold the comma-separated ordered list of attached fids + // This form input (id = edit-attachlist) will hold the comma-separated + // ordered list of attached fids. We need to store the attachments (which + // might not yet been saved to the database) in two cases: + // 1) A form_validate fails when trying to save/preview. The form is not + // rebuild by form_alter, but form values are refilled. #value => '' would + // delete the attachlist collected so far, so use #default_value => '' instead. $form['new']['attachlist'] = array( '#type' => 'hidden', - '#value' => ''); + '#default_value' => '', + ); + // 2) If form_validate didn't fail, the form is rebuild with form_alter + // and the value is reset. But we have the list in the POST parameters. + // Use default_value here, too, so that it can still be changed if we get + // a form_validate error *after* a successful preview. + if ($_POST['attachlist']) { + $form['new']['attachlist']['#default_value'] = $_POST['attachlist']; + } return $form; } @@ -974,14 +1187,14 @@ $trees[$key] = webfm_tree($root_dir, $current); } } else { - $err .= t('root directory not set for @role role ', array('@role' => $webfm_access_roles[$key])); + $err .= t('root directory not set for @role ', array('@role' => $webfm_access_roles[$key])); } } } else { $err = t('no root directory set in WebFM settings for this role'); } } - + if(count($trees)) { webfm_json(array('status' => TRUE, 'tree' => $trees, 'current' => $webfm_root_path, 'admin' => $webfm_perm == WEBFM_ADMIN, 'err' => $err)); } else { @@ -1005,6 +1218,14 @@ (array_key_exists($root_role, $webfm_roots))) { $current = "/".$root; } + else if(($root = variable_get('webfm_user_dir', '')) && + (array_key_exists($root_role, $webfm_roots))) { + // User dir can be tokenized + if (function_exists('token_replace')) { + $root = token_replace($root, 'user', $user); + } + $current = "/".$root; + } } if(!isset($current)) { webfm_json(array('status' => FALSE, 'data' => 'unknown tree')); @@ -1019,6 +1240,7 @@ $tree = webfm_tree($root_dir, $current); webfm_json(array('status' => isset($tree) ? TRUE : FALSE, 'tree' => $tree, 'current' => $current, 'admin' => $webfm_perm == WEBFM_ADMIN)); } + exit(); break; @@ -1031,7 +1253,7 @@ webfm_json(array('status' => FALSE, 'data' => 'illegal read dir')); exit(); } - + // Test access rights $perm_flag = FALSE; if($webfm_perm == WEBFM_ADMIN) { @@ -1042,6 +1264,7 @@ } else { // If WEBFM_USER, test that read path is inside a legit root dir $webfm_roots = webfm_get_root_dirs(); + foreach($webfm_roots as $key => $sub_root) { // The read path must be contained within a legitimate role root dir for this user if($sub_root && webfm_check_path($param0, $sub_root)) { @@ -1084,7 +1307,7 @@ if(is_dir($source)) { //Only admins can delete directories (and contained files) - if($webfm_perm == WEBFM_ADMIN) { + if($webfm_perm == WEBFM_ADMIN || webfm_path_access($source)) { $err_arr[] = array(); $ret = webfm_delete_dir_recur($source, TRUE, $err_arr); webfm_json(array('status' => $ret, 'data' => $err_arr)); @@ -1133,7 +1356,7 @@ //Create new directory case "mkdir": //Only admins can create directories - if($webfm_perm == WEBFM_ADMIN) { + //if($webfm_perm == WEBFM_ADMIN) { if(isset($_POST["param0"])) { $source = $root_dir.trim(rawurldecode($_POST["param0"])); $dest = t("New_Folder"); @@ -1142,14 +1365,14 @@ // exixts in current folder $ret = webfm_mkdir($source, $dest, TRUE, $err_arr); webfm_json(array('status' => $ret, 'data' => $err_arr)); - // if($ret) + // if($ret){ // unset($_SESSION['tree_'.$webfm_root_path]); } else { webfm_json(array('status' => FALSE, 'data' => 'insufficient params')); } - } else { - webfm_json(array('status' => FALSE, 'data' => 'permission denied')); - } + //} else { + // webfm_json(array('status' => FALSE, 'data' => 'permission denied')); + //} exit(); break; @@ -1158,7 +1381,7 @@ if(isset($_POST["param0"]) && isset($_POST["param1"])) { $source = $root_dir.trim(rawurldecode($_POST["param0"])); $dest = $root_dir.trim(rawurldecode($_POST["param1"])); - if(is_dir($source) && ($webfm_perm != WEBFM_ADMIN)) { + if(is_dir($source) && !webfm_path_access($source)) { //Only admins can manipulate directories webfm_json(array('status' => FALSE, 'data' => 'permission denied')); exit(); @@ -1191,7 +1414,8 @@ if(!ereg('\.\.', $dest)) { $err_arr[] = array(); //rename permissions inside webfm_rename - $ret = webfm_rename($source, $dest, ($webfm_perm == WEBFM_USER) ? $user->uid : 1, $err_arr); + //$ret = webfm_rename($source, $dest, ($webfm_perm == WEBFM_USER) ? $user->uid : 1, $err_arr); + $ret = webfm_rename($source, $dest, $user->uid, $err_arr); webfm_json(array('status' => $ret, 'data' => $err_arr)); } else { webfm_json(array('status' => FALSE, 'data' => 'illegal name')); @@ -1350,14 +1574,43 @@ case "attach": global $node; if(isset($_POST["param0"])) { + // If $_POST["param1"] is set, we are in a preview or validation failure + // and have fids stored in the edit-attachments form, those that we fetched + // from the database before and those which are not yet attached (during + // a preview). Those fids are transfered through param1. + if (isset($_POST["param1"])) { + $fids = trim(strtolower(rawurldecode($_POST["param1"]))); + webfm_json(array('status' => TRUE, 'data' => webfm_get_temp_attachments($fids), 'admin' => 'attach')); + exit(); + break; + } + // Here we are in the edit form for the first time. + // For comments, the URL is "comment/edit/$cid" instead of node/$nid/edit. + // Unify those. We must distinguish between new and existing nodes/comments, + // because for new nodes webfm passed a string instead of a nid which was + // interpreted as nid=0. But now with comments, we *have* entries with nid=0 + // in the table, so avoid fetching those. + // For new nodes and comments we can't get attachments from the database. $node_str = trim(strtolower(rawurldecode($_POST["param0"]))); - // the 'node' var passed via AJAX is the action attribute of id=node-form - if (($node_num = strstr($node_str, 'node/')) !== FALSE) { - $node_arr = explode("/", $node_num); - //'admin' is true (allow drag&drop) since only owners of a node can edit it - webfm_json(array('status' => TRUE, 'data' => webfm_get_attachments($node_arr[1]), 'admin' => 'attach')); - } else { - webfm_json(array('status' => FALSE, 'data' => 'illegal path')); + // We edit an existing node: Fetch from database. + if (strpos($node_str, '/edit')) { + $node_str = str_replace('/edit', '', $node_str); + $selector = 'nid'; + // The 'node' var passed via AJAX is the action attribute of id=node-form. + if (($node_num = strstr($node_str, 'node/')) == FALSE) { + $node_num = strstr($node_str, 'comment/'); + $selector = 'cid'; + } + if ($node_num) { + $node_arr = explode("/", $node_num); + // 'admin' is true (allow drag&drop) since only owners of a node can edit it. + webfm_json(array('status' => TRUE, 'data' => webfm_get_attachments($node_arr[1], $selector), 'admin' => 'attach')); + } + else { + webfm_json(array('status' => FALSE, 'data' => 'illegal path')); + exit(); + break; + } } } else { webfm_json(array('status' => FALSE, 'data' => 'insufficient params')); @@ -1478,6 +1731,66 @@ exit(); break; +/////////////////////////////////////////////////////////////////////////////// + + case "unzip": + if(isset($_POST["param0"])) { + $fid = rawurldecode($_POST["param0"]); + if(($file = webfm_get_file_record($fid)) !== FALSE) { + if($webfm_perm == WEBFM_ADMIN || + $user->uid == $file->uid || + webfm_file_mod_access($file)) { + + $zip = new ZipArchive; + $dir = dirname($file->fpath); + + // Extract files... + if ($zip->open($file->fpath) === TRUE) { + $zip->extractTo($dir); + $zip->close(); + + // now add files to database... + $zip = zip_open($file->fpath); + if ($zip) { + while ($zip_entry = zip_read($zip)) { + $filename = basename(zip_entry_name($zip_entry)); + if(!webfm_insert_file($dir . '/' . $filename, $error)) { + webfm_json(array('status' => FALSE, 'data' => $error)); + } + } + zip_close($zip); + } + + /* + // and delete archive + $error = ""; + $ret = TRUE; + if(@unlink($source)) { + if($file && !webfm_dbdelete_file($file->fid)) { + webfm_json(array('status' => $ret, 'data' => 'webfm_dbdelete_file() fail for '.$source)); + } + } else if(file_exists($source)) { + webfm_json(array('status' => $ret, 'data' => $source.' could not be deleted')); + } + */ + webfm_json(array('status' => TRUE, 'data' => 'unzip success')); + } else { + webfm_json(array('status' => FALSE, 'data' => 'unzip failed')); + } + } else { + webfm_json(array('status' => FALSE, 'data' => 'permission denied')); + } + } else { + webfm_json(array('status' => FALSE, 'data' => 'file record not found')); + } + } else { + webfm_json(array('status' => FALSE, 'data' => 'insufficient params')); + } + exit(); + break; + +/////////////////////////////////////////////////////////////////////////////// + default: webfm_json(array('status' => FALSE, 'data' => 'illegal operation')); exit(); @@ -1836,8 +2149,24 @@ $webfm_roots[1] = "/".$path; } } + + // User directory + $path = variable_get('webfm_user_dir', ''); + // Allow tokenized paths. + if (function_exists('token_replace')) { + $path = token_replace($path, 'user', $user); + } + if(!empty($path)) { + // Prevent redundant trees for user directory common with a role root dir + if(!in_array($path, $webfm_roots)) { + $webfm_roots[$user->name] = "/".$path; + // Create directory if neccesarry + $userdir = file_directory_path() . '/' . variable_get('webfm_root_dir', '') . '/' . $path; + file_check_directory($userdir, FILE_CREATE_DIRECTORY); + } + } } - + return $webfm_roots; } @@ -2073,9 +2402,16 @@ return isset($tree)?$tree:''; } -function webfm_get_attachments($nid) { +function webfm_get_attachments($nid, $selector = 'nid') { + // If anything but a existing nid/cid is passed, it is equivalent to 0. This + // happened in webfm_ajax in the past. Although we fixed it there, make sure + // that we never run into this, because now there are entries with nid/cid=0. + if ($nid == 0) { + return array(); + } + $files = array(); - $file_result = db_query('SELECT * FROM {webfm_file} f INNER JOIN {webfm_attach} a ON f.fid = a.fid WHERE a.nid = %d ORDER BY a.weight', $nid); + $file_result = db_query("SELECT * FROM {webfm_file} f INNER JOIN {webfm_attach} a ON f.fid = a.fid WHERE a.%s = %d ORDER BY a.weight", $selector, $nid); while($file_record = db_fetch_object($file_result)) { $_file = new webfm_fdesc($file_record); if($_file->result == TRUE) { @@ -2086,6 +2422,26 @@ } /** + * Return file descriptors for an attachment that have not yet been saved. + * This happens on previewing posts. + * + * @param array $fids - a comma separated list of fids. + * @return array of file descriptors. + */ +function webfm_get_temp_attachments($fids) { + $files = array(); + $fids_arr = split(',', $fids); + $file_result = db_query('SELECT * FROM {webfm_file} WHERE fid in ('. db_placeholders($fids_arr) .')', $fids_arr); + while ($file_record = db_fetch_object($file_result)) { + $_file = new webfm_fdesc($file_record); + if ($_file->result == TRUE) { + $files[] = $_file; + } + } + return $files; +} + +/** * File description class */ class webfm_fdesc { @@ -2269,16 +2625,24 @@ } // Files that have been attached are alway considered public to whoever can - // access that node (nodeaccess security) + // access that node/comment (nodeaccess/commentaccess security). if($match == FALSE && $webfm_perm != WEBFM_ADMIN) { if($f->perm & WEBFM_FILE_ACCESS_PUBLIC_VIEW) { $match = TRUE; } else if($webfm_perm == WEBFM_USER || $webfm_perm == WEBFM_ATTACH_VIEW){ - //Check if the file is attached to a node - $query = 'SELECT nid FROM {webfm_attach} WHERE fid = %d'; + //Check if the file is attached to a node or comment. + $query = 'SELECT nid,cid FROM {webfm_attach} WHERE fid = %d'; $result = db_query($query, $f->fid); if($result !== FALSE) { while ($dbfid = db_fetch_array($result)) { + if ($dbfid['cid'] != 0 ) { + // For a comment, a user must be able to view the parent node and have "access_comments". + if (!user_access('access comments')) { + continue; + } + $comment = _comment_load($dbfid['cid']); + $dbfid['nid'] = $comment->nid; + } $node = node_load($dbfid['nid']); if (node_access('view', $node)) { $match = TRUE; @@ -2482,7 +2846,12 @@ if(!$role_ext_regex[$rid]) { $extensions = variable_get("webfm_extensions_".$rid, ''); - $role_ext_regex[$rid] = '/\.('. ereg_replace(' +', '|', preg_quote($extensions)) .')$/i'; + if ($extensions == '*') { + $role_ext_regex[$rid] = '//'; + } + else { + $role_ext_regex[$rid] = '/\.('. ereg_replace(' +', '|', preg_quote($extensions)) .')$/i'; + } } return $role_ext_regex[$rid]; @@ -2735,16 +3104,26 @@ * * @param int $nid * @param array $fids - could be a comma seperated string - we need to work that out - works both ways now + * @param int $cid - Optional, comment id. * @return TRUE if records were updated - NULL if no changes were required. */ -function webfm_dbupdate_attach($nid, $fids){ +function webfm_dbupdate_attach($nid, $fids, $cid = 0){ + if ($cid == 0 ) { + $id = $nid; + $selector = 'nid'; + } + else { + $id = $cid; + $selector = 'cid'; + } + $i = 0; - if(!webfm_check_attach_order($nid, $fids)){ //the new fids are different from the old ones - $query = "DELETE FROM {webfm_attach} WHERE nid = %d"; - if($result = db_query($query, $nid)){ + if (!webfm_check_attach_order($nid, $fids, $cid)) { //the new fids are different from the old ones + $query = "DELETE FROM {webfm_attach} WHERE %s = %d"; + if ($result = db_query($query, $selector, $id)) { foreach($fids as $fid){ if($fid) - webfm_dbinsert_attach($nid, $fid, $i++); + webfm_dbinsert_attach($nid, $fid, $i++, $cid); $flag = TRUE; } if($flag === TRUE) return TRUE; @@ -2757,12 +3136,21 @@ * * @param int $nid - node id * @param array $fids - array of file ids + * @param int $cid - Optional, comment id. * @return bool - TRUE if the attach order is the same - FALSE if it has changed */ -function webfm_check_attach_order($nid, $fids){ +function webfm_check_attach_order($nid, $fids, $cid = 0) { + if ($cid == 0 ) { + $id = $nid; + $selector = 'nid'; + } + else { + $id = $cid; + $selector = 'cid'; + } //check array against db result - $query = "SELECT fid FROM {webfm_attach} WHERE nid = %d ORDER BY weight"; - $result = db_query($query, $nid); + $query = "SELECT fid FROM {webfm_attach} WHERE %s = %d ORDER BY weight"; + $result = db_query($query, $selector, $id); $match = TRUE; $i = 0; @@ -2785,23 +3173,35 @@ * @param int $nid - node id * @param int $fid - file id from the webfm_file table * @param int $weight - weight value - * - * @return bool - TRUE if success - else FALSE - */ -function webfm_dbinsert_attach($nid, $fid, $weight){ - $query = 'SELECT * FROM {webfm_attach} WHERE nid = %d AND fid = %d'; - $result = db_query($query, $nid, $fid); + * @param int $cid - Optional, comment id. If given, $nid is set to 0 because + * an attachment is stored either for a node OR a comment. + * @return bool - TRUE if success - else FALSE + */ +function webfm_dbinsert_attach($nid, $fid, $weight, $cid = 0) { + if ($cid == 0 ) { + $selector = 'nid'; + $type = 'node'; + $id = $nid; + } + else { + $selector = 'cid'; + $type = 'comment'; + $id = $cid; + $nid = 0; + } + $query = "SELECT * FROM {webfm_attach} WHERE %s = %d AND fid = %d"; + $result = db_query($query, $selector, $id, $fid); $exist = FALSE; while ($exist = db_fetch_object($result)){ - drupal_set_message(t('File is already attached to this node.')); + drupal_set_message(t('File is already attached to this %t.', array('%t' => $type))); return FALSE; } if ($exist === FALSE) { //actually do the attachment if its not already attached.... - $query = 'INSERT INTO {webfm_attach} (nid, fid, weight) VALUES (%d, %d, %d)'; - $result = db_query($query, $nid, $fid, $weight); + $query = 'INSERT INTO {webfm_attach} (nid, fid, weight, cid) VALUES (%d, %d, %d, %d)'; + $result = db_query($query, $nid, $fid, $weight, $cid); if($result === FALSE) { - drupal_set_message(t('Query Failed: Could not attach files to node ') . $nid); + drupal_set_message(t('Query Failed: Could not attach files to %t %n', array('%t' => $type, '%n' => $id))); return FALSE; } else { return TRUE; @@ -2810,6 +3210,26 @@ } /** + * webfm_dbdelete_attachments - deletes nodes/comments file associations. + * + * @param int $id - Node or comment id. + * @param selector - Tell if we handle a node or a comment. + * @return bool - TRUE if success - else FALSE + * + */ +function webfm_dbdelete_attachments($id, $selector = 'nid') { + $type = ($selector == 'nid') ? 'node' : 'comment'; + $query = "DELETE FROM {webfm_attach} WHERE %s = %d"; + $result = db_query($query, $selector, $id); + if($result === FALSE) { + drupal_set_message(t('Failed to remove file attachments for deleted %type %id', + array('%type' => $type, '%id' => $id))); + return FALSE; + } + return TRUE; +} + +/** * webfm_dbdelete_attach - deletes node file associations given a nid and fid * * @param int $nid - node id diff -ru webfm-6.x-2.9-alpha2/webfm_file.inc webfm/webfm_file.inc --- webfm-6.x-2.9-alpha2/webfm_file.inc 2008-04-08 08:28:14.000000000 +0200 +++ webfm/webfm_file.inc 2008-08-15 17:25:00.000000000 +0200 @@ -89,8 +89,8 @@ } //Directory - //if target is a directory, new name is a unique path and we are an admin... - else if(is_dir($source) && !is_dir($dest) && ($uid == 1)) { + //if target is a directory, new name is a unique path and we have access to that dir... + else if(is_dir($source) && !is_dir($dest) && webfm_path_access($source)) { //if the target isn't read-only if(rename($source, $dest_temp)) { //directory rename is OK, back out, rename db files and rename dir again @@ -204,7 +204,7 @@ * */ function webfm_move($source, $dest, $uid, &$err_arr) { - if(is_dir($source) && $uid == 1) { + if(is_dir($source) && webfm_path_access($source)) { return webfm_move_dir_recur($source, $dest, $uid, FALSE, $err_arr); } elseif(is_file($source)) { $dest .= '/' . strrev(substr(strrev($source), 0, strpos(strrev($source), '/')));