We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
  • Now it's hooked to the unzip processor. Needed to add to the manager/assets/modext/widgets/system/modx.tree.directory.js file:
        ,unzipFile: function(item,e) {
            var node = this.cm.activeNode;
            MODx.msg.confirm({
                text: _('file_download_unzip')+ ' ' + node.attributes.id
                ,url: MODx.config.base_url+'core/model/modx/processors/system/filesys/file/unzip.php'
                ,params: {
                    action: 'unzip'
                    ,file: node.attributes.id
                    ,wctx: MODx.ctx || ''
                    ,source: this.getSource()
                }
                ,listeners: {
                    'success':{fn:this.refreshParentNode,scope:this}
                }
            });
        }

    Now the problem is that in the unzip.php processor $modx is undefined.
      Studying MODX in the desert - http://sottwell.com
      Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
      Join the Slack Community - http://modx.org
      • 40045
      • 534 Posts
      OK, that was a tricky one =)...thanks @Susan for setting me on the right track!

      basically I have it working like that:

      1. Go to Security > Access Controls > Policy Templates and add a new permission called "file_unzip" to the AdministratorTemplate (or better a new duplicated one, as this gets overwritten on upgrade...)
      2. Switch to the tab "Access policies" and edit the Administrator policy (also here the duplicating helps preventing loss of custom permissions when upgrading) and tick the new permission "file_unzip" (it's unticked by default, so you have to activate it) > to make sure its really active, maybe do a Security > Flush Permissions
      3. The media source policies are actually hardcoded (which sucks a bit...as we have to modify the core) in /core/model/modx/sources/modmediasource.class.php on line 279, we have to add a new array item at the end like that:

          /**
           * Get a list of permissions for browsing and utilizing the source. May be overridden to provide a custom
           * list of permissions.
           * @return array
           */
          public function getPermissions() {
              $this->permissions = array(
                  // stripped...
                  'file_unzip' => $this->xpdo->hasPermission('file_unzip'),
              );
              return $this->permissions;
          }
      


      if we don't do that, we will not be able to check if somebody has the file_unzip permission...

      4. add the "Unzip file" menu item to the tree context menu in core/model/modx/sources/modfilemediasource.class.php around line 265 like this:

                  if (pathinfo($file->getFilename(), PATHINFO_EXTENSION) === 'zip' && $this->hasPermission('file_unzip') && $canView) {
                      $menu[] = array(
                          'text' => $this->xpdo->lexicon('file_download_unzip'),
                          'handler' => 'this.unzipFile',
                      );
                  }
      


      this checks basically if it's a zip file and if the user has the permission to unzip files

      5. go to manager/assets/modext/widgets/system/modx.tree.directory.js at around line 409 and add this:

          ,unzipFile: function(item,e) {
              var node = this.cm.activeNode;
              MODx.msg.confirm({
                  text: _('file_download_unzip')+ ' ' + node.attributes.id
                  ,url: MODx.config.connectors_url + 'system/filesys.php'
                  ,params: {
                      action: 'unzipFile'
                      ,file: node.attributes.id
                      ,wctx: MODx.ctx || ''
                      ,source: this.getSource()
                      ,path: MODx.config.base_path + node.attributes.directory
                  }
                  ,listeners: {
                      'success':{fn:this.refreshParentNode,scope:this}
                  }
              });
          }
      


      the difference to Susans version here is, that 1. the url goes to the filesys.php connector instead of directly to the processor (which doesn't work if the core directory is not at the standard location, e.g. above webroot (what it should be for security reasons), this also solves the problem of modx not being available. This connector actually has a case of "unzipFile" and lives at connectors/system/filesys.php, so nothing to change here (code just to show what I mean):

      	case 'unzipFile':
      		$modx->request->handleRequest(array('location' => 'system/filesys/file','action' => 'unzip'));
      		break;
      


      the unzip processor actually also needs a path to be specified, that I also added.

      6. The biggest change is in that acutal unzip processor at core/model/modx/processors/system/filesys/file/unzip.php, I have no idea where this is used (happy if somebody could tell me) because the package manager uses its own (much more modern) method to unzip the transport package zip files, the code is in core/xpdo/transport/xpdotransport.class.php line 735, there it works like this...

          /**
           * Unpack a zip archive to a specified location.
           *
           * @uses compression.xPDOZip OR compression.PclZip
           * @todo Refactor this to be implemented in a service class external to xPDOTransport.
           *
           * @param xPDO &$xpdo A reference to an xPDO instance.
           * @param string $from An absolute file system location to a valid zip archive.
           * @param string $to A file system location to extract the contents of the archive to.
           * @return array|boolean An array of unpacked resources or false on failure.
           */
          public static function _unpack(& $xpdo, $from, $to) {
              $resources = false;
              if ($xpdo->getOption(xPDOTransport::ARCHIVE_WITH, null, 0) != xPDOTransport::ARCHIVE_WITH_PCLZIP && class_exists('ZipArchive', true) && $xpdo->loadClass('compression.xPDOZip', XPDO_CORE_PATH, true, true)) {
                  $archive = new xPDOZip($xpdo, $from);
                  if ($archive) {
                      $resources = $archive->unpack($to);
                      $archive->close();
                  }
              } elseif (class_exists('PclZip') || include(XPDO_CORE_PATH . 'compression/pclzip.lib.php')) {
                  $archive = new PclZip($from);
                  if ($archive) {
                      $resources = $archive->extract(PCLZIP_OPT_PATH, $to);
                  }
              }
              return $resources;
          }
      


      so I was asking myself if the unzip processor is used somewhere at all?? This question came up, because it did not work at all for me...it was creating UNDELETABLE (due to no permissions set) folders that had names like "folder1\subfolder\wtf\" (including the backslashes, yes...I'm on a very normal LAMP server), so I tried and tried until I came up with the modified code for unzip.php, which now looks like this:

      <?php
      /**
       * @package modx
       * @subpackage processors.system.filesys.file
       */
      
      if (!$modx->hasPermission('file_manager')) return $modx->error->failure($modx->lexicon('permission_denied'));
      
      $file = $scriptProperties['path'].$scriptProperties['file'];
      if (!is_writable($scriptProperties['path'])) {
      	return $modx->error->failure($modx->lexicon('file_err_unzip_invalid_path'));
      }
      
      if (!file_exists($file)) {
      	return $modx->error->failure($modx->lexicon('file_err_nf'));
      }
      
      if(!$err = @unzip(realpath($file),realpath($scriptProperties['path']))) {
      	return $modx->error->failure($modx->lexicon('file_err_unzip').($err === 0 ? $modx->lexicon('file_err_unzip_missing_lib') : ''));
      }
      
      function unzip($file, $path) {
      	global $modx;
      	// added by Raymond
      	$r = substr($path,strlen($path)-1,1);
      	if ($r!="\\"||$r!="/") $path .="/";
      	if (!extension_loaded('zip')) {
      	   if (strtoupper(substr(PHP_OS, 0,3) == 'WIN')) {
      			if(!@dl('php_zip.dll')) return 0;
      	   } else {
      			if(!@dl('zip.so')) return 0;
      	   }
      	}
      	// end mod
      	$zip = zip_open($file);
      	if ($zip) {
      		$old_umask = umask(0);
      
      		while ($zip_entry = zip_read($zip)) {
      			if (zip_entry_filesize($zip_entry) > 0) {
      				// str_replace must be used under windows to convert "/" into "\"	
      				$complete_path = $path.str_replace('/',DIRECTORY_SEPARATOR,dirname(zip_entry_name($zip_entry)));
      				$complete_name = $path.str_replace ('/',DIRECTORY_SEPARATOR,zip_entry_name($zip_entry));
      				if(!file_exists($complete_path)) {
      					$tmp = '';
      					foreach(explode(DIRECTORY_SEPARATOR,$complete_path) AS $k) {
      						$tmp .= $k.DIRECTORY_SEPARATOR;
      						if(!file_exists($tmp)) {
      							mkdir($tmp, octdec((!empty($modx->getOption('new_folder_permissions')) ? $modx->getOption('new_folder_permissions') : 0755)));
      						}
      					}
      				}
      				if (zip_entry_open($zip, $zip_entry, "r")) {
      					$fd = fopen($complete_name, 'w');
      					fwrite($fd, zip_entry_read($zip_entry, zip_entry_filesize($zip_entry)));
      					fclose($fd);
      					zip_entry_close($zip_entry);
      				}
      			}
      		}
      
      		umask($old_umask);
      		zip_close($zip);
      		return true;
      	}
      	zip_close($zip);
      }
      
      return $modx->error->success();
      


      honestly this still looks just horrible (compared to the xpdotransport.class.php version) and should be refactored completely and probably be outsourced to the modfilehandler.class.php...but at least now it works in my environment (no possibility to test on Windows ... glad if somebody can) ... hope it doesn't break anything which uses this processor (is there something using this, again...?)

      but if you did these changes, you should now have a context menu for zip files, which extracts them to the directory where the archive lives...it would be actually nice if we could specify a path (defaults to /) to unpack the archive...but no idea how to do this...probably somewhere in that "do you really want to unpack" dialog with a text input field??

      I'd actually like to have this built into the core, so Susan, as you did the main work on this, would you like to create a PR on github or should I?
      • Be my guest.

        As far as I can find, nothing at all uses that unzip.php processor. I suspect that it, along with the entries in the lexicon file, are what's left of a good intention that never actually got implemented. I vaguely remember some mentions years ago that the file browser was never developed as fully as intended.
          Studying MODX in the desert - http://sottwell.com
          Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
          Join the Slack Community - http://modx.org
          • 40045
          • 534 Posts
          yap, looks exactly like that^^=)...should we do a PR with this stuff? you? me?

          EDIT: PR done! https://github.com/modxcms/revolution/pull/11352 [ed. note: exside last edited this post 10 years ago.]
          • Ok, it's unzipping the file, but in the site root, and with the wrong permissions on folders (d---r---wx). The unzipped top-level folders are empty.

            1. Set up the permissions.
            2. Modified core/model/modx/sources/modmediasource.class.php
            3. Modified core/model/modx/sources/modfilemediasource.class.php
            4. Modified manager/assets/modext/widgets/system/modx.tree.directory.js
            5. Modified core/model/modx/processors/system/filesys/file/unzip.php

            Logged out, cleared the entire cache, logged back in. Just to be sure wink

            My zip file consists of two folders, css and js, the css folder containing further folders and files.
              Studying MODX in the desert - http://sottwell.com
              Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
              Join the Slack Community - http://modx.org
              • 40045
              • 534 Posts
              Quote from: sottwell at May 10, 2014, 12:11 AM
              Ok, it's unzipping the file, but in the site root, and with the wrong permissions on folders (d---r---wx). The unzipped top-level folders are empty.

              1. Set up the permissions.
              2. Modified core/model/modx/sources/modmediasource.class.php
              3. Modified core/model/modx/sources/modfilemediasource.class.php
              4. Modified manager/assets/modext/widgets/system/modx.tree.directory.js
              5. Modified core/model/modx/processors/system/filesys/file/unzip.php

              Logged out, cleared the entire cache, logged back in. Just to be sure wink

              My zip file consists of two folders, css and js, the css folder containing further folders and files.

              hm, what media source base path are you using? I tested with a custom one, e.g. base_path of media source "Media" is assets/site/media/...when I unzip the archive gets extracted into the same folder as the archive lives...have to investigate further I guess...
              • Just the default. The zip file is uploaded to assets/theme/.
                  Studying MODX in the desert - http://sottwell.com
                  Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
                  Join the Slack Community - http://modx.org
                • I created a Media Source, assets/theme/, and upload the zip file there. Now it unzips in the base of that media source, which is good, but the file permissions are still messed up.
                    Studying MODX in the desert - http://sottwell.com
                    Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
                    Join the Slack Community - http://modx.org
                  • Oops... I think I discovered what's using that uzip.php processor. Now my core/cache/logs folder has the same bad permissions.
                      Studying MODX in the desert - http://sottwell.com
                      Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
                      Join the Slack Community - http://modx.org
                    • Ran across something odd. Tried creating new_folder_permissions and new_file_permissions in System Settings, and no matter what I set the Area Lexicon Entry to it won't use a lexicon, even though setting_new_folder_permissions and setting_new_file_permissions are definitely in the setting.inc.php file. And updating the setting editing the name or description doesn't change those fields.
                        Studying MODX in the desert - http://sottwell.com
                        Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
                        Join the Slack Community - http://modx.org