Files
2024-12-17 17:34:10 +01:00

715 lines
21 KiB
PHP

<?php
/**
* Attachments component
*
* @package Attachments
* @subpackage Attachments_Component
*
* @copyright Copyright (C) 2007-2018 Jonathan M. Cameron, All Rights Reserved
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU/GPL
* @link http://joomlacode.org/gf/project/attachments/frs/
* @author Jonathan M. Cameron
*/
defined('_JEXEC') or die('Restricted access');
// Access check.
if (!JFactory::getUser()->authorise('core.admin', 'com_attachments')) {
return JError::raiseError(404, JText::_('JERROR_ALERTNOAUTHOR') . ' (ERR 67)');
}
/** Load the Attachments defines */
require_once(JPATH_SITE.'/components/com_attachments/defines.php');
/**
* A class for update functions
*
* @package Attachments
*/
class AttachmentsUpdate
{
/**
* Add icon filenames for all attachments missing an icon
*/
public static function add_icon_filenames()
{
require_once(JPATH_COMPONENT_SITE.'/file_types.php');
// Get all the attachment IDs
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('id, filename, file_type, icon_filename')->from('#__attachments');
$query->where('file_type IS NULL');
$db->setQuery($query);
$attachments = $db->loadObjectList();
if ( $db->getErrorNum() ) {
$errmsg = $db->stderr() . ' (ERR 68)';
JError::raiseError(500, $errmsg);
}
if ( count($attachments) == 0 ) {
return JText::_('ATTACH_NO_FILE_TYPE_FIELDS_NEED_UPDATING');
}
$IDs = array();
foreach ($attachments as $attachment) {
$IDs[] = $attachment->id;
}
// Update the icon file_types all the attachments (that do not have one already)
JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_attachments/tables');
$attachment = JTable::getInstance('Attachment', 'AttachmentsTable');
$numUpdated = 0;
foreach ($IDs as $id) {
$attachment->load($id);
// Only update those attachment records that don't already have an icon_filename
if ( JString::strlen( $attachment->icon_filename ) == 0 ) {
$new_icon_filename = AttachmentsFileTypes::icon_filename($attachment->filename,
$attachment->file_type);
if ( JString::strlen( $new_icon_filename) > 0 ) {
$attachment->icon_filename = $new_icon_filename;
if (!$attachment->store()) {
$errmsg = JText::sprintf('ATTACH_ERROR_ADDING_ICON_FILENAME_FOR_ATTACHMENT_S', $attachment->filename) .
' ' . $attachment->getError() . ' (ERR 69)';
JError::raiseError(500, $errsmg);
}
$numUpdated++;
}
}
}
return JText::sprintf( 'ATTACH_ADDED_ICON_FILENAMES_TO_N_ATTACHMENTS', $numUpdated );
}
/**
* Update dates for all attachments with null dates
*/
public static function update_null_dates()
{
$app = JFactory::getApplication();
// Get all the attachment IDs
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('*')->from('#__attachments');
$db->setQuery($query);
$attachments = $db->loadObjectList();
if ( $db->getErrorNum() ) {
$errmsg = $db->stderr() . ' (ERR 70)';
JError::raiseError(500, $errmsg);
}
if ( count($attachments) == 0 ) {
return 0;
}
// Update the dates for all the attachments
$numUpdated = 0;
foreach ($attachments as $attachment) {
// Update the new create and update dates if they are null
$updated = false;
$created = $attachment->created;
if ( is_null($created) || ($created == '') ) {
$cdate = JFactory::getDate(filemtime($attachment->filename_sys));
$created = $cdate->toSql();
$updated = true;
}
$mod_date = $attachment->modified;
if ( is_null($mod_date) || ($mod_date == '') ) {
$mdate = JFactory::getDate(filemtime($attachment->filename_sys));
$mod_date = $mdate->toSql();
$updated = true;
}
// Update the record
if ( $updated ) {
$query = $db->getQuery(true);
$query->update('#__attachments');
$query->set('modified=' . $db->quote($mod_date) . ', created=' . $db->quote($created));
$query->where('id = ' . (int)$attachment->id);
$db->setQuery($query);
if (!$db->query()) {
$errmsg = JText::sprintf('ATTACH_ERROR_UPDATING_NULL_DATE_FOR_ATTACHMENT_FILE_S',
$attachment->filename);
JError::raiseError(500, $errmsg . $db->stderr() . ' (ERR 71)');
}
$numUpdated++;
}
}
return $numUpdated;
}
/**
* Disable uninstallation of attachments when the Attachments component is
* uninstalled.
*
* This is accomplished by modifying the uninstall.mysql.sql table to
* comment out the line that deletes the attachments table. Note that
* this only affects the table, not the attachments files.
*/
public static function disable_sql_uninstall($dbtype = 'mysql')
{
jimport('joomla.filesystem.file');
// Construct the filenames
if ( $dbtype == 'mysqli' ) {
// Use the same MYSQL installation file for mysqli
$dbtype = 'mysql';
}
$filename = JPATH_COMPONENT_ADMINISTRATOR."/sql/uninstall.$dbtype.sql";
$tempfilename = $filename.'.tmp';
$msg = '';
// Read the content of the old version of the uninstall sql file
$contents = JFile::read($filename);
$lines = explode("\n", $contents);
$new_lines = Array();
for ($i=0; $i < count($lines); $i++) {
$line = JString::trim($lines[$i]);
if ( JString::strlen($line) != 0 ) {
if ( $line[0] != '#' ) {
$line = '# ' . $line;
}
$new_lines[] = $line;
}
}
// Overwrite the old file with a commented out version
$new_contents = implode("\n", $new_lines) . "\n";
JFile::write($tempfilename, $new_contents);
if ( ! JFile::copy( $tempfilename, $filename) ) {
$msg = JText::_('ATTACH_ERROR_UPDATING_FILE') . ": $filename!";
}
// Let the user know what happened
if ( $msg == '' ) {
$msg = JText::_('ATTACH_DISABLED_UNINSTALLING_MYSQL_ATTACHMENTS_TABLE');
}
return $msg;
}
/**
* Check an attachment filename and return relevant info
*/
private static function checkFilename($filename)
{
$finfo = new JObject();
// If it is a windows filename, convert to Linux format for analysis
$winfile = false;
$win_file_re = "|^(?P<drive>[a-zA-Z]{1}):(?P<filename>.*)$|";
if ( preg_match($win_file_re, $filename, $match) ) {
$winfile = true;
$filename = str_replace("\\", '/', $match['drive'] . ':' . $match['filename']);
}
$finfo->winfile = $winfile;
// Split the file into parts
$parts = explode('/', $filename);
$finfo->parts = $parts;
// See if it is old-style (pre Attachments 2.0)
$finfo->oldstyle = false;
$attachments_dir_name = 'attachments';
if ( $parts[count($parts)-2] == $attachments_dir_name ) {
$finfo->oldstyle = true;
}
// Get the path info
$pathinfo = pathinfo($filename);
$finfo->basename = $pathinfo['basename'];
$finfo->extension = $pathinfo['extension'];
// Construct the relative path for the current OS
$start = array_search($attachments_dir_name, $parts);
$relfile = '';
for ($i = $start; $i < count($parts); $i++) {
$relfile .= $parts[$i];
if ( $i < count($parts) - 1 ) {
$relfile .= '/';
}
}
$finfo->relfile = $relfile;
// Construct the non-prefix version of the filename (if oldstyle)
if ( $finfo->oldstyle ) {
$finfo->prefix = false;
if ( preg_match('|^[0-9]{3}_(?P<filename>.+\..+$)|', $finfo->basename, $match) ) {
$finfo->prefix = true;
$finfo->basename_no_prefix = $match['filename'];
}
}
return $finfo;
}
/**
* Regenerate the system filenames for all attachments.
*
* This function may need to run if the admin has moved the attachments
* from one computer to another and the actual file paths need to be
* updated.
*/
public static function regenerate_system_filenames()
{
require_once(JPATH_SITE.'/components/com_attachments/helper.php');
// Get the component parameters
jimport('joomla.application.component.helper');
$params = JComponentHelper::getParams('com_attachments');
// Define where the attachments go
$upload_url = AttachmentsDefines::$ATTACHMENTS_SUBDIR;
$upload_dir = JPATH_SITE . '/' . $upload_url;
// Get all the attachment IDs
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('id')->from('#__attachments')->where('uri_type=' . $db->quote('file'));
$db->setQuery($query);
$attachments = $db->loadObjectList();
if ( $db->getErrorNum() ) {
$errmsg = $db->stderr() . ' (ERR 72)';
JError::raiseError(500, $errmsg);
}
if ( count($attachments) == 0 ) {
return JText::_('ATTACH_NO_ATTACHMENTS_WITH_FILES');
}
$IDs = array();
foreach ($attachments as $attachment) {
$IDs[] = $attachment->id;
}
// Get the parent plugin manager
JPluginHelper::importPlugin('attachments');
$apm = getAttachmentsPluginManager();
// Update the system filenames for all the attachments
jimport('joomla.filesystem.file');
jimport('joomla.filesystem.folder');
JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_attachments/tables');
$attachment = JTable::getInstance('Attachment', 'AttachmentsTable');
$msg = '';
$numUpdated = 0;
$numMissing = 0;
foreach ($IDs as $id) {
$attachment->load($id);
// Get the actual parent id for this attachment
// (Needed because orphaned parent_id is null, which the Table loads as 1)
$query = $db->getQuery(true);
$query->select('parent_id')->from('#__attachments')->where('id = ' . (int)$id);
$db->setQuery($query, 0, 1);
$parent_id = $db->loadResult();
if ( $db->getErrorNum() ) {
$errmsg = JText::sprintf('ATTACH_ERROR_INVALID_PARENT_S_ID_N',
$attachment->parent_entity, $parent_id) . ' (ERR 73)';
JError::raiseError(500, $errmsg);
}
// Construct the updated system filename
$old_filename_sys = $attachment->filename_sys;
// Get info about the system filename
$finfo = AttachmentsUpdate::checkFilename($old_filename_sys);
$basename = $finfo->basename;
// Reconstruct the current system filename (in case of migrations)
$current_filename_sys = JPATH_SITE.'/'.$finfo->relfile;
// Get the parent object
$parent = $apm->getAttachmentsPlugin($attachment->parent_type);
if ( !JFile::exists($current_filename_sys) ) {
$msg .= JText::sprintf('ATTACH_ERROR_MISSING_ATTACHMENT_FILE_S',
$current_filename_sys) . "<br/>";
$numMissing++;
}
elseif ( !is_numeric($parent_id) ||
!$parent->parentExists($attachment->parent_id, $attachment->parent_entity ) ) {
$msg .= JText::sprintf('ATTACH_ERROR_MISSING_PARENT_FOR_ATTACHMENT_S',
$current_filename_sys) . "<br/>";
$numMissing++;
}
else {
// Construct the new system filename and url (based on entities, etc)
$newdir = $parent->getAttachmentPath($attachment->parent_entity, $attachment->parent_id, null);
$new_path = $upload_dir.'/'.$newdir;
if ( $finfo->oldstyle && $finfo->prefix ) {
$new_filename_sys = $new_path . $finfo->basename_no_prefix;
$attachment->filename = $finfo->basename_no_prefix;
$new_url = str_replace(DIRECTORY_SEPARATOR, '/', $upload_url . '/' . $newdir . $finfo->basename_no_prefix);
}
else {
$new_filename_sys = $new_path . $basename;
$new_url = str_replace(DIRECTORY_SEPARATOR, '/', $upload_url . '/' . $newdir . $basename);
}
// If we are on windows, fix the filename and URL
if ( DIRECTORY_SEPARATOR != '/' ) {
$new_filename_sys = str_replace('/', DIRECTORY_SEPARATOR, $new_filename_sys);
$new_url = str_replace(DIRECTORY_SEPARATOR, '/', $new_url);
}
// Make sure the target directory exists
if ( !JFile::exists($new_path) ) {
if ( !JFolder::create($new_path) ) {
$errmsg = JText::sprintf('ATTACH_ERROR_UNABLE_TO_SETUP_UPLOAD_DIR_S', $new_path) . ' (ERR 74)';
JError::raiseError(500, $errmsg);
}
AttachmentsHelper::write_empty_index_html($new_path);
}
// Move the file!
if ( !JFile::move($current_filename_sys, $new_filename_sys) ) {
$errmsg = JText::sprintf('ATTACH_ERROR_RENAMING_FILE_S_TO_S',
$old_filename_sys, $new_filename_sys) . ' (ERR 75)';
JError::raiseError(500, $errmsg);
}
// Verify the new system filename exists!
if ( !JFile::exists($new_filename_sys) ) {
$errmsg = JText::sprintf('ATTACH_ERROR_NEW_SYSTEM_FILENAME_S_NOT_FOUND',
$new_filename_sys) . ' (ERR 76)';
JError::raiseError(500, $errmsg);
}
// Update the record
$attachment->filename_sys = $new_filename_sys;
$attachment->url = $new_url;
if (!$attachment->store()) {
$errmsg = $attachment->getError() . ' (ERR 77)';
JError::raiseError(500, $errmsg);
}
$numUpdated++;
}
}
// Add warning if there are problem files
if ( $numMissing > 0 ) {
$msg = JText::sprintf('ATTACH_ERROR_N_FILES_MISSING', $numMissing) . "<br/>" . $msg . "&nbsp;<br/>";
}
return $msg . JText::sprintf( 'ATTACH_REGENERATED_SYSTEM_FILENAMES_FOR_N_ATTACHMENTS',
$numUpdated );
}
/**
* Remove spaces from the system filenames for all attachments
*
* The spaces are replaces with underscores '_'
*/
public static function remove_spaces_from_system_filenames()
{
// Get the component parameters
jimport('joomla.application.component.helper');
$params = JComponentHelper::getParams('com_attachments');
// Define where the attachments go
$upload_dir = JPATH_SITE.'/'.AttachmentsDefines::$ATTACHMENTS_SUBDIR;
// Get all the attachment IDs
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('id')->from('#__attachments')->where('uri_type=' . $db->quote('file'));
$db->setQuery($query);
$attachments = $db->loadObjectList();
if ( $db->getErrorNum() ) {
$errmsg = $db->stderr() . ' (ERR 78)';
JError::raiseError(500, $errmsg);
}
if ( count($attachments) == 0 ) {
return JText::_('ATTACH_NO_ATTACHMENTS_WITH_FILES');
}
$IDs = array();
foreach ($attachments as $attachment) {
$IDs[] = $attachment->id;
}
// Get ready to rename files
jimport( 'joomla.filesystem.file' );
// Update the system filenames for all the attachments
JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_attachments/tables');
$attachment = JTable::getInstance('Attachment', 'AttachmentsTable');
$numUpdated = 0;
foreach ($IDs as $id) {
$attachment->load($id);
// Make sure the file exists
$old_filename_sys = $attachment->filename_sys;
if ( !JFile::exists( $old_filename_sys ) ) {
echo JText::sprintf('ATTACH_ERROR_FILE_S_NOT_FOUND_ON_SERVER', $old_filename_sys);
exit();
}
// Construct the new system filename
$filename_info = pathinfo($old_filename_sys);
$basename = $filename_info['basename'];
$filename_sys = $upload_dir.'/'.$basename;
$new_basename = str_replace(' ', '_', $basename);
$new_filename_sys = $filename_info['dirname'].'/'.$new_basename;
// If the filename has not changed, do not change anything
if ( $new_filename_sys == $old_filename_sys ) {
continue;
}
// Rename the file
if ( !JFile::move($old_filename_sys, $new_filename_sys) ) {
echo JText::sprintf('ATTACH_ERROR_RENAMING_FILE_S_TO_S',
$old_filename_sys, $new_filename_sys);
exit();
}
// Construct the new URL (figuire it out from the system filename)
$attachments_dir = str_replace(JPATH_SITE, '', $filename_info['dirname']);
$dirend_chars = DIRECTORY_SEPARATOR.'/';
$attachments_dir = JString::trim($attachments_dir, $dirend_chars);
$attachments_dir = str_replace(DIRECTORY_SEPARATOR, '/', $attachments_dir);
$new_url = $attachments_dir . '/' . $new_basename;
// Update the record
$attachment->filename_sys = $new_filename_sys;
$attachment->filename = $new_basename;
$attachment->url = $new_url;
if (!$attachment->store()) {
$errmsg = $attachment->getError() . ' (ERR 79)';
JError::raiseError(500, $errmsg);
}
$numUpdated++;
}
return JText::sprintf( 'ATTACH_UPDATED_N_ATTACHMENTS', $numUpdated );
}
/**
* Update the file sizes for all attachments (only applies to files)
*/
public static function update_file_sizes()
{
// Get the component parameters
jimport('joomla.application.component.helper');
$params = JComponentHelper::getParams('com_attachments');
// Define where the attachments go
$upload_dir = JPATH_SITE.'/'.AttachmentsDefines::$ATTACHMENTS_SUBDIR;
// Get all the attachment IDs
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('id')->from('#__attachments')->where('uri_type=' . $db->quote('file'));
$db->setQuery($query);
$attachments = $db->loadObjectList();
if ( $db->getErrorNum() ) {
$errmsg = $db->stderr() . ' (ERR 80)';
JError::raiseError(500, $errmsg);
}
if ( count($attachments) == 0 ) {
return JText::_('ATTACH_NO_ATTACHMENTS_WITH_FILES');
}
$IDs = array();
foreach ($attachments as $attachment) {
$IDs[] = $attachment->id;
}
// Update the system filenames for all the attachments
JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_attachments/tables');
$attachment = JTable::getInstance('Attachment', 'AttachmentsTable');
$numUpdated = 0;
foreach ($IDs as $id) {
$attachment->load($id);
// Update the file size
$attachment->file_size = filesize($attachment->filename_sys);
// Update the record
if (!$attachment->store()) {
$errmsg = $attachment->getError() . ' (ERR 81)';
JError::raiseError(500, $errmsg);
}
$numUpdated++;
}
return JText::sprintf( 'ATTACH_UPDATED_FILE_SIZES_FOR_N_ATTACHMENTS', $numUpdated );
}
/**
* Check all files and make sure they exist
*/
public static function check_files_existance()
{
jimport('joomla.filesystem.file');
$msg = '';
// Get the component parameters
jimport('joomla.application.component.helper');
$params = JComponentHelper::getParams('com_attachments');
// Get all the attachment IDs
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('id')->from('#__attachments')->where('uri_type=' . $db->quote('file'));
$db->setQuery($query);
$attachments = $db->loadObjectList();
if ( $db->getErrorNum() ) {
$errmsg = $db->stderr() . ' (ERR 82)';
JError::raiseError(500, $errmsg);
}
if ( count($attachments) == 0 ) {
return JText::_('ATTACH_NO_ATTACHMENTS_WITH_FILES');
}
$IDs = array();
foreach ($attachments as $attachment) {
$IDs[] = $attachment->id;
}
// Update the system filenames for all the attachments
JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_attachments/tables');
$attachment = JTable::getInstance('Attachment', 'AttachmentsTable');
$numMissing = 0;
$numChecked = 0;
foreach ($IDs as $id) {
$attachment->load($id);
if ( !JFile::exists($attachment->filename_sys) ) {
$msg .= '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' .
$attachment->filename_sys . '<br >';
$numMissing++;
}
$numChecked++;
}
if ( $msg ) {
$msg = ':<br />' . $msg;
}
$msg = JText::sprintf( 'ATTACH_CHECKED_N_ATTACHMENT_FILES_M_MISSING', $numChecked, $numMissing ) . $msg;
return $msg;
}
/**
* Validate all URLS and update their "valid" status
*/
public static function validate_urls()
{
// Get the component parameters
jimport('joomla.application.component.helper');
$params = JComponentHelper::getParams('com_attachments');
// Get all the attachment IDs
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('id')->from('#__attachments')->where('uri_type=' . $db->quote('url'));
$db->setQuery($query);
$attachments = $db->loadObjectList();
if ( $db->getErrorNum() ) {
$errmsg = $db->stderr() . ' (ERR 83)';
JError::raiseError(500, $errmsg);
}
if ( count($attachments) == 0 ) {
return JText::_('ATTACH_NO_ATTACHMENTS_WITH_URLS');
}
$IDs = array();
foreach ($attachments as $attachment) {
$IDs[] = $attachment->id;
}
// Update the system filenames for all the attachments
require_once(JPATH_SITE.'/components/com_attachments/helper.php');
JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_attachments/tables');
$attachment = JTable::getInstance('Attachment', 'AttachmentsTable');
$numUpdated = 0;
$numChecked = 0;
foreach ($IDs as $id) {
$attachment->load($id);
$a = new JObject();
AttachmentsHelper::get_url_info($attachment->url, $a, false, false);
if ( $attachment->url_valid != $a->url_valid ) {
$attachment->url_valid = $a->url_valid;
// Maybe update the file info with fresh info
if ( $a->url_valid ) {
$attachment->file_size = $a->file_size;
$attachment->file_type = $a->file_type;
}
// Update the record
if (!$attachment->store()) {
$errmsg = $attachment->getError() . ' (ERR 84)';
JError::raiseError(500, $errmsg);
}
$numUpdated++;
}
$numChecked++;
}
return JText::sprintf( 'ATTACH_VALIDATED_N_URL_ATTACHMENTS_M_CHANGED', $numChecked, $numUpdated );
}
/**
* Validate all URLS and update their "valid" status
*/
public static function installAttachmentsPermissions($verbose = true)
{
jimport('joomla.access.rules');
$app = JFactory::getApplication();
// Get the root rules
$root = JTable::getInstance('asset');
$root->loadByName('root.1');
$root_rules = new JAccessRules($root->rules);
// Define the new rules
$new_rules = new JAccessRules(AttachmentsDefines::$DEFAULT_ATTACHMENTS_ACL_PERMISSIONS);
// Merge the rules into default rules and save it
$root_rules->merge($new_rules);
$root->rules = (string)$root_rules;
if ( $root->store() ) {
if ( $verbose ) {
$app->enqueueMessage(JText::_('ATTACH_INSTALLED_DEFAULT_ATTACHMENTS_ASSET_RULES'), 'message');
}
}
else {
if ( $verbose ) {
$app->enqueueMessage(JText::_('ATTACH_INSTALLING_DEFAULT_ATTACHMENTS_ASSET_RULES_FAILED'), 'message');
}
}
}
}