[opencms-dev] some bug fixes and enhancements for OpenCms 5.0.1

Sven Bonorden bonorden at getit.de
Wed Jul 7 16:00:01 CEST 2004


Hi all,

during our work with OpenCms 5.0.1 we had to change many classes of the 
OpenCms core itself.
Our main work is commercial and very customer-specific, but the core 
changes are of course Open Source.
I collected the bug fixes and enhancements so anyone having similar 
problems can use our solutions.

Maybe some of our modifications could also be added to version 6 but I 
did not check if they work in the current CVS head as we only used 5.0.1.
That's why I did not add the complete java files or patch files so the 
core developers can decide what may be necessary.

Bug fixes (details see attached file, some of them are already in Bugzilla)
- When restoring an old version of a resource, the properties of this 
version are not restored (#361)
- Log channels in property file have wrong names and so they are never used.
- When a folder is copied and the destination folder already exists, the 
copying fails (#362)
- You cannot add new resource types if their classes are in a module
- Moving or renaming a folder resource does not move the /system/bodies 
folder (#363)
- Cms always sents Mails in charset ISO8859-1
- Link substitution ignores urls in <input src="..."> and <form 
action="...">
- The source editor cannot display special characters from Eastern Europe
- Some German spelling errors, mainly to fix different spellings of 
"Ressource"
- CmsRequestContext.getSession fails with NullPointerException if no 
request is available
- Scheduler does not start jobs if the minute is 0 (#74)
- when creating a resource, the property values (i.e. title) are not 
encoded correctly on some systems
- It is impossible to disable the scheduler when creating a new CmsObject
- In CmsResourceBroker some methods change the file-state after saving 
the file. That does not make sense.
- If copying a resource, the owner and group are also copied from the 
source.
- CmsResourceBroker.readAllFileHeadersForHist throws a CmsException if 
the resource has been deleted (#348)
- Two different backup versions of a resource are considered equal
- Trying to send a mail from a scheduled job fails with 
NullPointerException (#364)
- Access checks for read and visible return a wrong result if the user 
has these privileges via his group (#365)

Enhancements (details see attached file)
- Change the group for a resource recursively from CmsShell
- Methods to access a project with a specific name from CmsShell
- Methods to read properties from a backup version for a resource
- CmsSetupUtils cannot read Property values containing a comma
- Functionality for users to be logged in without a password
- Workplace Popup for folder tree always starts with root folder
- You cannot get a specific project by its name
- write a property if the property definition does not exist
- find a folder with a specific name searching only through a part of VFS
- find all resources in a folder that have been published after a 
specific date
- get the resource type of a deleted resource
- folder tree popup to show not only the name, but also the title

Kind regards,
Sven

-- 
Sven Bonorden
Softwareentwicklung und Beratung          mailto:bonorden at getit.de

-----
GETIT - Gesellschaft für Technologie- und Informationstransfer mbH

Emil-Figge-Straße 76-80                        http://www.getit.de
44227 Dortmund                                mailto:info at getit.de

-------------- next part --------------
Bugfixes for OpenCms 5.0.1:
-------------------------------

This file lists bugfixes made by GETIT GmbH for productional use of OpenCms 5.0.1.

---------------------------------------------------------------------------------------------------

Bug 361:
When restoring an old version of a resource, the properties of this version are not restored.

com.opencms.file.genericSql.CmsDbAccess:
- new method:
    /**
     * Returns a list of all history properties of a file or folder.<p>
     *
     * @param resourceId the id of the resource
     * @param resource the resource to read the properties from
     * @param resourceType the type of the resource
     *
     * @return a Map of Strings representing the properties of the resource
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public HashMap readPropertiesForHist(CmsResource resource, int resourceType)
        throws CmsException {

        HashMap returnValue = new HashMap();
        ResultSet result = null;
        PreparedStatement statement = null;
        Connection con = null;
        String usedPool;
        int resourceId = resource.getResourceId();
        //int onlineProject = getOnlineProject(projectId).getId();
        usedPool = m_poolNameBackup;
        try {
           con = DriverManager.getConnection(usedPool);
            // create project
            statement = con.prepareStatement(m_cq.get("C_PROPERTIES_READALL_BACKUP"));
            statement.setInt(1, resourceId);
            statement.setInt(2, resourceType);
            result = statement.executeQuery();
            while(result.next()) {
                 returnValue.put(result.getString(m_cq.get("C_PROPERTYDEF_NAME")),
                                 result.getString(m_cq.get("C_PROPERTY_VALUE")));
            }
        } catch( SQLException exc ) {
            throw new CmsException("[" + this.getClass().getName() + "] " + exc.getMessage(),
                CmsException.C_SQL_ERROR, exc);
        } finally {
            // close all db-resources
            if(result != null) {
                try {
                    result.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
            if(statement != null) {
                try {
                    statement.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
            if(con != null) {
                try {
                    con.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
        }
        return(returnValue);
    }

com.opencms.file.genericSql.CmsResourceBroker:
- new constant:
    protected static final String C_CACHE_ALL_BACKUP_PROPERTIES = "__CACHE_ALL_BACKUP_PROPERTIES__";

- new method:
    /**
     * Looks up all properties for a resource with optional direcory upward cascading.<p>
     * 
     * 
     * @param currentUser the current user
     * @param currentProject the current project of the user
     * @param resource the resource to look up the property for
     * @param siteRoot the current site root
     * @param search if <code>true</code>, the properties will also be looked up on all parent folders
     *   and the results will be merged, if <code>false</code> not (ie. normal property lookup)
     * @return Map of Strings representing all properties of the resource
     * @throws CmsException in case there where problems reading the properties
     */
    public Map readPropertiesForHist(CmsUser currentUser, CmsProject currentProject, String resource, String siteRoot, boolean search, int version_id) 
    throws CmsException {
        // read the resource from history
        CmsResource res = readFileForHist (currentUser, currentProject, version_id,resource);
        
        search = search && (siteRoot != null);
        // check if we have the result already cached
        HashMap value = null;
        
        String cacheKey = getCacheKey(C_CACHE_ALL_BACKUP_PROPERTIES + search+version_id, null, new CmsProject(currentProject.getId(), -1), res.getResourceName());
        value = (HashMap)m_propertyCache.get(cacheKey);
        
        if (value == null) {
            // result not cached, let's look it up in the DB
            if (search) {
                boolean cont;
                siteRoot += "/";
                value = new HashMap();
                HashMap parentValue;
                do {
                    parentValue = (HashMap)readProperties(currentUser, currentProject, resource, siteRoot, false);
                    parentValue.putAll(value);
                    value.clear();
                    value.putAll(parentValue);
                    resource = CmsResource.getParent(resource);
                    cont = (! siteRoot.equals(resource));
                } while (cont);
            } else {
                value = m_dbAccess.readPropertiesForHist(res, res.getType());
            }  
            // store the result in the cache
            m_propertyCache.put(cacheKey, value);
        }
        return (Map)value.clone();
    }    

- change method restoreResource:
old version:
            writeFile(currentUser, currentProject, newFile);

new version:
            writeFile(currentUser, currentProject, newFile);
            deleteAllProperties(currentUser,currentProject,newFile.getResourceName());
            Map oldProperties = readPropertiesForHist(currentUser,currentProject,newFile.getResourceName(),null,false,versionId);
            writeProperties(currentUser,currentProject,newFile.getResourceName(),oldProperties);

com.opencms.file.I_CmsResourceBroker:
- new method:
    /**
     * Looks up all properties from a backup resource with optional directory upward cascading.<p>
     * 
     * <b>Security:</b>
     * Only a user is granted who has the right to read the resource.
     * 
     * @param currentUser the current user
     * @param currentProject the current project of the user
     * @param resource the resource to look up the property for
     * @param siteroot the site root where to stop the cascading
     * @param search if <code>true</code>, the properties will also be looked up on all parent folders
     *   and the results will be merged, if <code>false</code> not (ie. normal property lookup)
     * @param versionId the version id containing the defined properties
     * @return Map of Strings representing all properties of the resource
     * @throws CmsException in case there where problems reading the properties
     */
	public Map readPropertiesForHist(CmsUser currentUser, CmsProject currentProject, String resource, String siteRoot, boolean search, int versionId) throws CmsException;    

com.opencms.file.CmsObject:
- new method:
    /**
     * Looks up all properties for a resource.<p>
     * 
     * @param resource the resource to look up the property for
     * @param versionId the requeste version of the resource
     * @return Map of Strings representing all properties of the resource
     * @throws CmsException in case there where problems reading the properties
     */  
    public Map readProperties(String resource,int versionId) throws CmsException {
    	return m_rb.readPropertiesForHist(m_context.currentUser(), m_context.currentProject(), m_context.getSiteRoot(resource), m_context.getSiteRoot(), false,versionId);
    }

---------------------------------------------------------------------------------------------------

Log channels in property file have wrong names and so they are never used.

opencms.properties:
old version:
    log.channel.module_debug=false
    log.channel.module_info=true
    log.channel.module_critical=true

new version:
    log.channel.modules_debug=false
    log.channel.modules_info=true
    log.channel.modules_critical=true

---------------------------------------------------------------------------------------------------

Bug 362
When a folder is copied and the destination folder already exists, the copying fails.
We added a check before creating the destination.

com.opencms.file.genericSql.CmsResourceBroker:
- change method copyFile:
old version:
                m_dbAccess.createFolder(currentUser,currentProject, onlineProject(currentUser, currentProject), folder,cmsFolder.getResourceId(),destination);
new version:
                // Check if folder already exists before trying to create it
                CmsFolder oldFolder=null;
                try {
                 oldFolder = readFolder(currentUser,currentProject,destination);
                } catch (CmsException e) {
                }
                if (oldFolder==null) m_dbAccess.createFolder(currentUser,currentProject, onlineProject(currentUser, currentProject), folder,cmsFolder.getResourceId(),destination);

---------------------------------------------------------------------------------------------------

You cannot add new resource types if their classes are in a module.
If you add resource types to registry.xml and the module is not installed yet, 
OpenCms stops with a ClassNotFoundException

com.opencms.file.genericSql.CmsResourceBroker:
- change method getAllResourceTypes
old version:
                    }catch(Exception e){
                        e.printStackTrace();
                        throw new CmsException("[" + this.getClass().getName() + "] Error while getting ResourceType: " 
                            + (String)resTypeNames.elementAt(i) + " from registry ", CmsException.C_UNKNOWN_EXCEPTION );
                    }

new version:
                    }catch(Exception e){
                        e.printStackTrace();
                        if (e instanceof ClassNotFoundException) {
                            // ignore this exception. 
                            // maybe a module has added a new resource type to registry.xml
                            // but the classes are not yet installed 
                        } else {
                            throw new CmsException("[" + this.getClass().getName() + "] Error while getting ResourceType: " 
                                + (String)resTypeNames.elementAt(i) + " from registry ", CmsException.C_UNKNOWN_EXCEPTION );
                        }
                    }

---------------------------------------------------------------------------------------------------

Bug 363:
Moving or renaming a folder resource does not move the /system/bodies folder so bodies are lost for ever if you rename or move a folder.
We fixed the bug by removing redundant code.

com.opencms.file.CmsResourceTypeFolder.java:
- change Method moveResource:
old Version:
    public void moveResource(CmsObject cms, String source, String destination) throws CmsException{
        // it is a folder so we need the end /
        destination = destination +"/";
       // we have to copy the folder and all resources in the folder
        Vector allSubFolders = new Vector();
        Vector allSubFiles   = new Vector();
        getAllResources(cms, source, allSubFiles, allSubFolders);
        if(!cms.accessWrite(source)){
            throw new CmsException(source, CmsException.C_NO_ACCESS);
        }
        // first copy the folder
        cms.doCopyFolder(source, destination);
        // now copy the subfolders
        for (int i=0; i<allSubFolders.size(); i++){
            CmsFolder curFolder = (CmsFolder) allSubFolders.elementAt(i);
            if(curFolder.getState() != C_STATE_DELETED){
                String curDestination = destination + curFolder.getAbsolutePath().substring(source.length());
                cms.doCopyFolder(curFolder.getAbsolutePath(), curDestination );
            }
        }
        // now move the files
        for (int i=0; i<allSubFiles.size(); i++){
            CmsFile curFile = (CmsFile)allSubFiles.elementAt(i);
            if(curFile.getState() != C_STATE_DELETED){
                String curDest = destination + curFile.getAbsolutePath().substring(source.length());
                cms.moveResource(curFile.getAbsolutePath(), curDest);
            }
        }
        // finaly remove the original folders
        deleteResource(cms, source);
    }

new version:
    public void moveResource(CmsObject cms, String source, String destination) throws CmsException{
       
        copyResource(cms, source, destination, true);
        // finally remove the original folders
        deleteResource(cms, source);
    }

- change Method renameResource:
old version:
    public void renameResource(CmsObject cms, String oldname, String newname) throws CmsException{
        // first of all check the new name
        validResourcename(newname.replace('/','\n'));
       // we have to copy the folder and all resources in the folder
        Vector allSubFolders = new Vector();
        Vector allSubFiles   = new Vector();
        getAllResources(cms, oldname, allSubFiles, allSubFolders);
        String parent = ((CmsResource)cms.readFileHeader(oldname)).getParent();
        if(!cms.accessWrite(oldname)){
            throw new CmsException(oldname, CmsException.C_NO_ACCESS);
        }
        if(!newname.endsWith("/")){
            newname = newname+"/";
        }
        // first copy the folder
        cms.doCopyFolder(oldname, parent + newname);
        // now copy the subfolders
        for (int i=0; i<allSubFolders.size(); i++){
            CmsFolder curFolder = (CmsFolder) allSubFolders.elementAt(i);
            if(curFolder.getState() != C_STATE_DELETED){
                String curDestination = parent + newname
                                        + curFolder.getAbsolutePath().substring(oldname.length());
                cms.doCopyFolder(curFolder.getAbsolutePath(), curDestination );
            }
        }
        // now move the files
        for (int i=0; i<allSubFiles.size(); i++){
            CmsFile curFile = (CmsFile)allSubFiles.elementAt(i);
            if(curFile.getState() != C_STATE_DELETED){
                String curDest = parent + newname
                                + curFile.getAbsolutePath().substring(oldname.length());
                cms.moveResource(curFile.getAbsolutePath(), curDest);
            }
        }
        // finaly remove the original folders
        deleteResource(cms, oldname);

    }

new version:
    public void renameResource(CmsObject cms, String oldname, String newname) throws CmsException{
        // first of all check the new name
        validResourcename(newname.replace('/','\n'));

        String parent = ((CmsResource)cms.readFileHeader(oldname)).getParent();
        if(!cms.accessWrite(oldname)){
            throw new CmsException(oldname, CmsException.C_NO_ACCESS);
        }

        moveResource(cms, oldname, parent + newname);
    }

---------------------------------------------------------------------------------------------------

Cms always sents Mails in charset ISO8859-1 so special chars not contained in this charset will get messed up.
We changed the class to work with UTF-8 which is our default charset - would be better to choose charset dynamically
from setup...
See also Bug #83. We did not check the solution suggested there.

com.opencms.defaults.CmsMail
- change method buildMessage
old version:
        // Default encoding for new mail message
        String mail_encoding = "ISO-8859-1";
        
        ...
        
        msg.setSentDate(new Date());
        return msg;
        
version for UTF-8:
        // Default encoding for new mail message
        String mail_encoding = "UTF-8"; 
        
        ...
        
        msg.setSentDate(new Date());
        // following statement added because otherwise mail is always sent
        // with Content-Type "text/plain" in the header
        // and charset defaults to ISO-8859-1                 
        msg.setHeader("Content-Type", c_TYPE + "; charset=UTF-8");
        
        return msg;

---------------------------------------------------------------------------------------------------

Link substitution ignores urls in <input src="..."> and <form action="...">

com.opencms.util.LinkSubstitution.java:
- change static String m_converterConfiguration
old version:
            "   <replacetags usedefaults=\"true\">"+
            "       <tag name=\"img\" attrib=\"src\" replacestarttag=\"]]><LINK><![CDATA[$parameter$]]></LINK><![CDATA[\" parameter=\"src\" replaceparamattr=\"true\"/>"+
            "       <tag name=\"a\" attrib=\"href\" replacestarttag=\"]]><LINK><![CDATA[$parameter$]]></LINK><![CDATA[\" replaceendtag=\"</a>\" parameter=\"href\" replaceparamattr=\"true\"/>"+
            "   </replacetags>"+

new version:
            "   <replacetags usedefaults=\"true\">"+
            "       <tag name=\"img\" attrib=\"src\" replacestarttag=\"]]><LINK><![CDATA[$parameter$]]></LINK><![CDATA[\" parameter=\"src\" replaceparamattr=\"true\"/>"+
            "       <tag name=\"input\" attrib=\"src\" replacestarttag=\"]]><LINK><![CDATA[$parameter$]]></LINK><![CDATA[\" parameter=\"src\" replaceparamattr=\"true\"/>"+
            "       <tag name=\"form\" attrib=\"action\" replacestarttag=\"]]><LINK><![CDATA[$parameter$]]></LINK><![CDATA[\" parameter=\"action\" replaceparamattr=\"true\"/>"+  
            "       <tag name=\"a\" attrib=\"href\" replacestarttag=\"]]><LINK><![CDATA[$parameter$]]></LINK><![CDATA[\" replaceendtag=\"</a>\" parameter=\"href\" replaceparamattr=\"true\"/>"+
            "   </replacetags>"+

---------------------------------------------------------------------------------------------------

The source editor cannot display special characters from Eastern Europe. We changed the default charset from FixedSys to Courier New 
which contains many more characters.

/etc/ocsetup/vfs/system/workplace/templates/edit_text_main:
old version:
	<!-- write the text area -->
	<script language="JavaScript">
	<!--      
		str = '<textarea wrap="off" name="edit1" onfocus="setTextDelayed();" style="width:100%; height:100%; font-family:Fixedsys, monospace"></textarea>';
		document.write(str);
	//-->
	</script>

    new version:
	<!-- write the text area -->
	<script language="JavaScript">
	<!--      
		str = '<textarea wrap="off" name="edit1" onfocus="setTextDelayed();" style="width:100%; height:100%; font-family:Courier New, monospace"></textarea>';
		document.write(str);
	//-->

---------------------------------------------------------------------------------------------------

Some German spelling errors, mainly to fix different spellings of "Ressource".

com.opencms.workplace.workplace_de.properties:
old version:
report.publish_resource_begin=Gebe Resource(n) frei ...
report.publish_resource_end=... die Resource(n) wurde(n) freigegeben
error.message.accessdenied=Leider konnte auf die Resource nicht zugegriffen werden.
module.context.admin=Administieren
flexcache.admin.label3=Zeige gecachte Resources mit Schlüsseln an
flexcache.admin.label4=Zeige gecachte Resources mit Schlüsseln und Variationen an
flexcache.admin.cached_resources=Momentan gecachte Resourcen
flexcache.admin.cached_keys_variants=Gecachte Resourcen mit Schlüsseln und Variationen:
flexcache.admin.cached_keys=Gecachte Resourcen mit Schlüsseln:
flexcache.admin.online=Online Resourcen:
flexcache.admin.offline=Offline Resourcen:
flexcache.admin.err_online=Keine gecachten Online Resourcen!
flexcache.admin.err_offline=Keine gecachten Offline Resourcen!
flexcache.admin.resource=Resource

new version:
report.publish_resource_begin=Gebe Ressource(n) frei ...
report.publish_resource_end=... die Ressource(n) wurde(n) freigegeben
error.message.accessdenied=Leider konnte auf die Ressource nicht zugegriffen werden.
module.context.admin=Administrieren
flexcache.admin.label3=Zeige gecachte Ressourcen mit Schlüsseln an
flexcache.admin.label4=Zeige gecachte Ressourcen mit Schlüsseln und Variationen an
flexcache.admin.cached_resources=Momentan gecachte Ressourcen
flexcache.admin.cached_keys_variants=Gecachte Ressourcen mit Schlüsseln und Variationen:
flexcache.admin.cached_keys=Gecachte Ressourcen mit Schlüsseln:
flexcache.admin.online=Online Ressourcen:
flexcache.admin.offline=Offline Ressourcen:
flexcache.admin.err_online=Keine gecachten Online Ressourcen!
flexcache.admin.err_offline=Keine gecachten Offline Ressourcen!
flexcache.admin.resource=Ressource

---------------------------------------------------------------------------------------------------

CmsRequestContext.getSession fails with NullPointerException if no request is available 
which is possible if called in scheduled jobs.

com.opencms.file.CmsRequestContext:
- change method getSession
old version:
    public I_CmsSession getSession(boolean value) {
        HttpSession session =
            ((HttpServletRequest) m_req.getOriginalRequest()).getSession(value);
        if (session != null) {
            return (I_CmsSession) new CmsSession(session);
        } else {
            return null;
        }
    }

new version:
    public I_CmsSession getSession(boolean value) {
        // added checks for null.
        if (m_req == null || m_req.getOriginalRequest() == null) {
            return null;
        }
        
        HttpSession session =
            ((HttpServletRequest) m_req.getOriginalRequest()).getSession(value);
        if (session != null) {
            return (I_CmsSession) new CmsSession(session);
        } else {
            return null;
        }
    }

---------------------------------------------------------------------------------------------------

Fix for Bugzilla #74: Scheduler does not start jobs if the minute is 0.

com.opencms.core.CmsCronEntry.java:
- change Method check:
old version:
        if(m_minute != C_ASTERIX) // check the minute
    		if(!isBetween(lastTime.get(Calendar.MINUTE), m_minute, now.get(Calendar.MINUTE)))
	    		return false;

new version:
        if(m_minute != C_ASTERIX) { // check the minute
            // changed to correct condition if lastTime was in last hour 
            // (minute 59 < minute 0)
            int lastTimeMinute = lastTime.get(Calendar.MINUTE);
            int nowMinute = now.get(Calendar.MINUTE);
            while (lastTimeMinute > nowMinute) { 
                lastTimeMinute -= 60;
            }
   	    if(!isBetween(lastTimeMinute, m_minute, nowMinute))
	    		return false;
        }

---------------------------------------------------------------------------------------------------

On some systems, when creating a resource, the entered strings for property values (i.e. title) are not
encoded correctly. German umlauts are replaced by question marks.
We had this problem on a SUSE Linux using an IBM JDK and Java system property file.encoding=UTF-8. 
This could be solved by always explicitly specifying an encoding when converting Strings to bytes and back.

com.opencms.utils.Encoder:
- change method redecodeUriComponent:
old version:
    public static String redecodeUriComponent(String input) {
       if (input == null) return input;
       return new String(changeEncoding(input.getBytes(), C_URI_ENCODING, A_OpenCms.getDefaultEncoding())); 
    }

new version:
    public static String redecodeUriComponent(String input) {
       if (input == null) return input;
       // added encoding for getBytes and new String because otherwise 
       // platform default encoding is used which may destroy some characters 
       try {
           return new String(changeEncoding(input.getBytes(C_URI_ENCODING), C_URI_ENCODING, A_OpenCms.getDefaultEncoding()), 
                             A_OpenCms.getDefaultEncoding()); 
       } catch (UnsupportedEncodingException e) {
           return new String(changeEncoding(input.getBytes(), C_URI_ENCODING, A_OpenCms.getDefaultEncoding())); 
       }
    }

---------------------------------------------------------------------------------------------------

It is impossible to disable the scheduler when creating a new CmsObject because this property is always read from 
system properties, ignoring the specified properties (this behaviour is different from all other properties)

com.opencms.core.OpenCms:
- change constructor OpenCms
old version:
            // if the System property opencms.disableScheduler is set to true, don't start scheduling
            if(!new Boolean(System.getProperty("opencms.disableScheduler")).booleanValue()) {
                // now initialise the OpenCms scheduler to launch cronjobs
                m_table = new CmsCronTable(m_resourceBroker.readCronTable(null, null));
                m_scheduler = new CmsCronScheduler(this, m_table);
                if(C_LOGGING && isLogging(C_OPENCMS_INIT)) log(C_OPENCMS_INIT, ". OpenCms scheduler    : enabled");
            } else {
                if(C_LOGGING && isLogging(C_OPENCMS_INIT)) log(C_OPENCMS_INIT, ". OpenCms scheduler    : disabled");
            }
new version:
            // if the property opencms.disableScheduler is set to true, don't start scheduling
            if(!conf.getBoolean("opencms.disableScheduler", false)) {
                // now initialise the OpenCms scheduler to launch cronjobs
                m_table = new CmsCronTable(m_resourceBroker.readCronTable(null, null));
                m_scheduler = new CmsCronScheduler(this, m_table);
                if(C_LOGGING && isLogging(C_OPENCMS_INIT)) log(C_OPENCMS_INIT, ". OpenCms scheduler    : enabled");
            } else {
                if(C_LOGGING && isLogging(C_OPENCMS_INIT)) log(C_OPENCMS_INIT, ". OpenCms scheduler    : disabled");
            }

---------------------------------------------------------------------------------------------------

In CmsResourceBroker some methods change the file-state after saving the file. That does not make sense.

com.opencms.file.genericSql.CmsResourceBroker:
- change method writeProperty:
old version:
        if(res.isFile()){
            m_dbAccess.writeFileHeader(currentProject, (CmsFile) res, true, currentUser.getId());
            if (res.getState()==C_STATE_UNCHANGED) {
                res.setState(C_STATE_CHANGED);
            }
        } else {
            if (res.getState()==C_STATE_UNCHANGED) {
                res.setState(C_STATE_CHANGED);
            }
            m_dbAccess.writeFolder(currentProject, readFolder(currentUser,currentProject, resource), true, currentUser.getId());
        }
new version:
        if(res.isFile()){
            if (res.getState()==C_STATE_UNCHANGED) {
                res.setState(C_STATE_CHANGED);
            }
            m_dbAccess.writeFileHeader(currentProject, (CmsFile) res, true, currentUser.getId());
        } else {
            if (res.getState()==C_STATE_UNCHANGED) {
                res.setState(C_STATE_CHANGED);
            }
            m_dbAccess.writeFolder(currentProject, readFolder(currentUser,currentProject, resource), true, currentUser.getId());
        }


The same holds for methods chgrp, chmod, chown, chtype, deleteProperty, writeFile, writeFileHeader.



---------------------------------------------------------------------------------------------------

If copying a resource, the owner and group are also copied from the source. So if you copy a resource not owned by yourself,
you may not be allowed to change it afterwards. As you are not the owner, you cannot change the owner to yourself after copying.
When copying files in a real file system, the owner is changed so we implemented the same behaviour for VFS.
As moving and renaming is implemented by copy & delete, the same holds for move and rename.

This change could only be realized at a level below the access checks, because otherwise we would not have the privilege to change the owner.
Therefore, we had to change some signatures.

com.opencms.file.genericSql.CmsDbAccess
- add return value to method copyFile
old version:
    /**
     * Copies the file.
     *
     * @param project The project in which the resource will be used.
     * @param onlineProject The online project of the OpenCms.
     * @param userId The id of the user who wants to copy the file.
     * @param source The complete path of the sourcefile.
     * @param parentId The parentId of the resource.
     * @param destination The complete path of the destinationfile.
     *
     * @throws CmsException Throws CmsException if operation was not succesful.
     */
     public void copyFile(CmsProject project,
                          CmsProject onlineProject,
                          int userId,
                          String source,
                          int parentId,
                          String destination)
         throws CmsException {
         CmsFile file;

         // read sourcefile
         file=readFile(project.getId(),onlineProject.getId(),source);
         // create destination file
         createFile(project,onlineProject,file,userId,parentId,destination);
     }

new version:
    /**
     * Copies the file.
     *
     * @param project The project in which the resource will be used.
     * @param onlineProject The online project of the OpenCms.
     * @param userId The id of the user who wants to copy the file.
     * @param source The complete path of the sourcefile.
     * @param parentId The parentId of the resource.
     * @param destination The complete path of the destinationfile.
     * 
     * @return the created destination file
     * @throws CmsException Throws CmsException if operation was not succesful.
     */
     public CmsFile copyFile(CmsProject project,
                          CmsProject onlineProject,
                          int userId,
                          String source,
                          int parentId,
                          String destination)
         throws CmsException {
         CmsFile file;

         // read sourcefile
         file=readFile(project.getId(),onlineProject.getId(),source);
         // create destination file
         return createFile(project,onlineProject,file,userId,parentId,destination);
     }



com.opencms.file.genericSql.CmsResourceBroker:
- change method copyFile:
old version:
    /**
     * Copies a file in the Cms. <br>
     *
     * <B>Security:</B>
     * Access is granted, if:
     * <ul>
     * <li>the user has access to the project</li>
     * <li>the user can read the sourceresource</li>
     * <li>the user can create the destinationresource</li>
     * <li>the destinationresource doesn't exist</li>
     * </ul>
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param source The complete path of the sourcefile.
     * @param destination The complete path to the destination.
     *
     * @throws CmsException  Throws CmsException if operation was not succesful.
     */
    public void copyFile(CmsUser currentUser, CmsProject currentProject,
                         String source, String destination)
        throws CmsException {

        // the name of the new file.
        String filename;
        // the name of the folder.
        String foldername;

        // checks, if the destinateion is valid, if not it throws a exception
        validFilename(destination.replace('/', 'a'));

        // read the source-file, to check readaccess
        CmsResource file = readFileHeader(currentUser, currentProject, source);

        // split the destination into file and foldername
        if (destination.endsWith("/")) {
            filename = file.getName();
            foldername = destination;
        }else{
            foldername = destination.substring(0, destination.lastIndexOf("/")+1);
            filename = destination.substring(destination.lastIndexOf("/")+1,
                                             destination.length());
        }

        CmsFolder cmsFolder = readFolder(currentUser,currentProject, foldername);
        if( accessCreate(currentUser, currentProject, (CmsResource)cmsFolder) ) {
            if(( accessOther(file, C_ACCESS_PUBLIC_WRITE) ||
                accessOwner(currentUser, currentProject, file, C_ACCESS_OWNER_WRITE) ||
                accessGroup(currentUser, currentProject, file, C_ACCESS_GROUP_WRITE) )){
                // write-acces  was granted - copy the file and the metainfos
                m_dbAccess.copyFile(currentProject, onlineProject(currentUser, currentProject),
                              currentUser.getId(),source,cmsFolder.getResourceId(), foldername + filename);

                this.clearResourceCache(foldername + filename, currentProject, currentUser);
                // copy the metainfos
                lockResource(currentUser, currentProject, destination, true);
                writeProperties(currentUser,currentProject, destination,
                            readProperties(currentUser, currentProject, file.getResourceName(), null, false));
                m_accessCache.clear();
                // inform about the file-system-change
                fileSystemChanged(file.isFolder());
            } else {
                throw new CmsException("[" + this.getClass().getName() + "] " + source,
                    CmsException.C_NO_ACCESS);
            }
        } else {
            throw new CmsException("[" + this.getClass().getName() + "] " + destination,
                CmsException.C_NO_ACCESS);
        }
    }

new version:
    /**
     * Copies a file in the Cms. <br>
     * The owner and the group of the copied file are set to current user and group. <br>
     *
     * <B>Security:</B>
     * Access is granted, if:
     * <ul>
     * <li>the user has access to the project</li>
     * <li>the user can read the sourceresource</li>
     * <li>the user can create the destinationresource</li>
     * <li>the destinationresource doesn't exist</li>
     * </ul>
     *
     * @param currentUser The user who requested this method.
     * @param currentGroup The current group of the user.
     * @param currentProject The current project of the user.
     * @param source The complete path of the sourcefile.
     * @param destination The complete path to the destination.
     *
     * @throws CmsException  Throws CmsException if operation was not succesful.
     */
    public void copyFile(CmsUser currentUser, CmsGroup currentGroup, CmsProject currentProject, 
                         String source, String destination)
        throws CmsException {

        // the name of the new file.
        String filename;
        // the name of the folder.
        String foldername;

        // checks, if the destinateion is valid, if not it throws a exception
        validFilename(destination.replace('/', 'a'));

        // read the source-file, to check readaccess
        CmsResource file = readFileHeader(currentUser, currentProject, source);

        // split the destination into file and foldername
        if (destination.endsWith("/")) {
            filename = file.getName();
            foldername = destination;
        }else{
            foldername = destination.substring(0, destination.lastIndexOf("/")+1);
            filename = destination.substring(destination.lastIndexOf("/")+1,
                                             destination.length());
        }

        CmsFolder cmsFolder = readFolder(currentUser,currentProject, foldername);
        if( accessCreate(currentUser, currentProject, (CmsResource)cmsFolder) ) {
            if(( accessOther(file, C_ACCESS_PUBLIC_WRITE) ||
                accessOwner(currentUser, currentProject, file, C_ACCESS_OWNER_WRITE) ||
                accessGroup(currentUser, currentProject, file, C_ACCESS_GROUP_WRITE) )){
                // write-acces  was granted - copy the file and the metainfos
                CmsFile destinationFile = m_dbAccess.copyFile(currentProject, onlineProject(currentUser, currentProject),
                              currentUser.getId(),source,cmsFolder.getResourceId(), foldername + filename);

                this.clearResourceCache(foldername + filename, currentProject, currentUser);
                // copy the metainfos
                lockResource(currentUser, currentProject, destination, true);
                destinationFile.setLocked(currentUser.getId());
                destinationFile.setLockedInProject(currentProject.getId());
                writeProperties(currentUser,currentProject, destination,
                    readProperties(currentUser, currentProject, file.getResourceName(), null, false));
                // set user and group to currentUser (otherwise the currentUser could not change it
                // because the copy is also owned by source owner).
                // we cannot use chown/chgrp here because of access checks!
                destinationFile.setUserId(currentUser.getId());
                destinationFile.setGroupId(currentGroup.getId());
                m_dbAccess.writeFileHeader(currentProject, destinationFile, true, currentUser.getId());
                this.clearResourceCache(filename, currentProject, currentUser);
                m_accessCache.clear();
                // inform about the file-system-change
                fileSystemChanged(file.isFolder());
            } else {
                throw new CmsException("[" + this.getClass().getName() + "] " + source,
                    CmsException.C_NO_ACCESS);
            }
        } else {
            throw new CmsException("[" + this.getClass().getName() + "] " + destination,
                CmsException.C_NO_ACCESS);
        }
    }

- change method copyFolder
old version:
     /**
     * Copies a folder in the Cms. <br>
     *
     * <B>Security:</B>
     * Access is granted, if:
     * <ul>
     * <li>the user has access to the project</li>
     * <li>the user can read the sourceresource</li>
     * <li>the user can create the destinationresource</li>
     * <li>the destinationresource doesn't exist</li>
     * </ul>
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param source The complete path of the sourcefolder.
     * @param destination The complete path to the destination.
     *
     * @throws CmsException  Throws CmsException if operation was not succesful.
     */
    public void copyFolder(CmsUser currentUser, CmsProject currentProject,
                           String source, String destination)
        throws CmsException {

        // the name of the folder.
        String foldername;

        // checks, if the destinateion is valid, if not it throws a exception
        validFilename(destination.replace('/', 'a'));
        foldername = destination.substring(0, destination.substring(0,destination.length()-1).lastIndexOf("/")+1);
        CmsFolder cmsFolder = readFolder(currentUser,currentProject, foldername);
        if( accessCreate(currentUser, currentProject, (CmsResource)cmsFolder) ) {
            // write-acces  was granted - copy the folder and the properties
            CmsFolder folder=readFolder(currentUser,currentProject,source);
            // check write access to the folder that has to be copied
            if(( accessOther((CmsResource)folder, C_ACCESS_PUBLIC_WRITE) ||
                accessOwner(currentUser, currentProject, (CmsResource)folder, C_ACCESS_OWNER_WRITE) ||
                accessGroup(currentUser, currentProject, (CmsResource)folder, C_ACCESS_GROUP_WRITE) )){
                m_dbAccess.createFolder(currentUser,currentProject, onlineProject(currentUser, currentProject), folder,cmsFolder.getResourceId(),destination);
                this.clearResourceCache(destination, currentProject, currentUser);
                // copy the properties
                lockResource(currentUser, currentProject, destination, true);
                writeProperties(currentUser,currentProject, destination,
                            readProperties(currentUser, currentProject, folder.getResourceName(), null, false));
                m_resourceListCache.clear();
                m_accessCache.clear();
                // inform about the file-system-change
                fileSystemChanged(true);
            } else {
                throw new CmsException("[" + this.getClass().getName() + "] " + source,
                    CmsException.C_ACCESS_DENIED);
            }
        } else {
            throw new CmsException("[" + this.getClass().getName() + "] " + destination,
                CmsException.C_ACCESS_DENIED);
        }

    }

new version:
     /**
     * Copies a folder in the Cms. <br>
     * The owner and the group of the copied folder are set to current user and group. <br>
     *
     * <B>Security:</B>
     * Access is granted, if:
     * <ul>
     * <li>the user has access to the project</li>
     * <li>the user can read the sourceresource</li>
     * <li>the user can create the destinationresource</li>
     * <li>the destinationresource doesn't exist</li>
     * </ul>
     *
     * @param currentUser The user who requested this method.
     * @param currentGroup The current group of the user.
     * @param currentProject The current project of the user.
     * @param source The complete path of the sourcefolder.
     * @param destination The complete path to the destination.
     *
     * @throws CmsException  Throws CmsException if operation was not succesful.
     */
    public void copyFolder(CmsUser currentUser, CmsGroup currentGroup, CmsProject currentProject,
                           String source, String destination)
        throws CmsException {

        // the name of the folder.
        String foldername;

        // checks, if the destinateion is valid, if not it throws a exception
        validFilename(destination.replace('/', 'a'));
        foldername = destination.substring(0, destination.substring(0,destination.length()-1).lastIndexOf("/")+1);
        CmsFolder cmsFolder = readFolder(currentUser,currentProject, foldername);
        if( accessCreate(currentUser, currentProject, (CmsResource)cmsFolder) ) {
            // write-acces  was granted - copy the folder and the properties
            CmsFolder folder=readFolder(currentUser,currentProject,source);
            // check write access to the folder that has to be copied
            if(( accessOther((CmsResource)folder, C_ACCESS_PUBLIC_WRITE) ||
                accessOwner(currentUser, currentProject, (CmsResource)folder, C_ACCESS_OWNER_WRITE) ||
                accessGroup(currentUser, currentProject, (CmsResource)folder, C_ACCESS_GROUP_WRITE) )){
                // Check if folder already exists before trying to create it
                CmsFolder destinationFolder=null;
                try {
                    destinationFolder = readFolder(currentUser,currentProject,destination);
                } catch (CmsException e) {
                }
                if (destinationFolder==null) { 
                    destinationFolder = m_dbAccess.createFolder(currentUser,currentProject, onlineProject(currentUser, currentProject), folder,cmsFolder.getResourceId(),destination);} 
                this.clearResourceCache(destination, currentProject, currentUser);
                // copy the properties
                lockResource(currentUser, currentProject, destination, true);
                destinationFolder.setLocked(currentUser.getId());
                destinationFolder.setLockedInProject(currentProject.getId());
                writeProperties(currentUser,currentProject, destination,
                            readProperties(currentUser, currentProject, folder.getResourceName(), null, false));
                // set user and group to currentUser (otherwise the currentUser could not change it
                // because the copy is also owned by source owner).
                // we cannot use chown/chgrp here because of access checks!
                destinationFolder.setUserId(currentUser.getId());
                destinationFolder.setGroupId(currentGroup.getId());
                m_dbAccess.writeFolder(currentProject, destinationFolder, true, currentUser.getId());
                this.clearResourceCache(destination, currentProject, currentUser);

                m_resourceListCache.clear();
                m_accessCache.clear();
                // inform about the file-system-change
                fileSystemChanged(true);
            } else {
                throw new CmsException("[" + this.getClass().getName() + "] " + source,
                    CmsException.C_ACCESS_DENIED);
            }
        } else {
            throw new CmsException("[" + this.getClass().getName() + "] " + destination,
                CmsException.C_ACCESS_DENIED);
        }

    }

- change method moveFile:
old version:
    /**
     * Moves the file.
     *
     * This operation includes a copy and a delete operation. These operations
     * are done with their security-checks.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param source The complete path of the sourcefile.
     * @param destination The complete path of the destinationfile.
     *
     * @throws CmsException will be thrown, if the file couldn't be moved.
     * The CmsException will also be thrown, if the user has not the rights
     * for this resource.
     */
    public void moveFile(CmsUser currentUser, CmsProject currentProject, String source, String destination) throws CmsException {

        // read the file to check access
        CmsResource file = readFileHeader(currentUser,currentProject, source);

        // has the user write-access?
        if (accessWrite(currentUser, currentProject, file)) {

            // first copy the file, this may ends with an exception
            copyFile(currentUser, currentProject, source, destination);

            // then delete the source-file, this may end with an exception
            // => the file was only copied, not moved!
            deleteFile(currentUser, currentProject, source);
            // inform about the file-system-change
            fileSystemChanged(file.isFolder());
        } else {
            throw new CmsException("[" + this.getClass().getName() + "] " + source, CmsException.C_NO_ACCESS);
        }
    }

new version:
    /**
     * Moves the file.
     *
     * This operation includes a copy and a delete operation. These operations
     * are done with their security-checks.
     *
     * @param currentUser The user who requested this method.
     * @param currentGroup The current group of the user.
     * @param currentProject The current project of the user.
     * @param source The complete path of the sourcefile.
     * @param destination The complete path of the destinationfile.
     *
     * @throws CmsException will be thrown, if the file couldn't be moved.
     * The CmsException will also be thrown, if the user has not the rights
     * for this resource.
     */
    public void moveFile(CmsUser currentUser, CmsGroup currentGroup, CmsProject currentProject, String source, String destination) throws CmsException {

        // read the file to check access
        CmsResource file = readFileHeader(currentUser,currentProject, source);

        // has the user write-access?
        if (accessWrite(currentUser, currentProject, file)) {

            // first copy the file, this may ends with an exception
            copyFile(currentUser, currentGroup, currentProject, source, destination);

            // then delete the source-file, this may end with an exception
            // => the file was only copied, not moved!
            deleteFile(currentUser, currentProject, source);
            // inform about the file-system-change
            fileSystemChanged(file.isFolder());
        } else {
            throw new CmsException("[" + this.getClass().getName() + "] " + source, CmsException.C_NO_ACCESS);
        }
    }

- change method renameFile
old version:
    /**
     * Renames the file to a new name. <br>
     *
     * Rename can only be done in an offline project. To rename a file, the following
     * steps have to be done:
     * <ul>
     * <li> Copy the file with the oldname to a file with the new name, the state
     * of the new file is set to NEW (2).
     * <ul>
     * <li> If the state of the original file is UNCHANGED (0), the file content of the
     * file is read from the online project. </li>
     * <li> If the state of the original file is CHANGED (1) or NEW (2) the file content
     * of the file is read from the offline project. </li>
     * </ul>
     * </li>
     * <li> Set the state of the old file to DELETED (3). </li>
     * </ul>
     *
     * <B>Security:</B>
     * Access is granted, if:
     * <ul>
     * <li>the user has access to the project</li>
     * <li>the user can write the resource</li>
     * <li>the resource is locked by the callingUser</li>
     * </ul>
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param oldname The complete path to the resource which will be renamed.
     * @param newname The new name of the resource (CmsUser callingUser, No path information allowed).
     *
     * @throws CmsException  Throws CmsException if operation was not succesful.
     */
    public void renameFile(CmsUser currentUser, CmsProject currentProject, String oldname, String newname) throws CmsException {
    
        // read the old file
        CmsResource file = readFileHeader(currentUser, currentProject, oldname);
    
        // checks, if the newname is valid, if not it throws a exception
        validFilename(newname);
    
        // has the user write-access?
        if (accessWrite(currentUser, currentProject, file)) {
            String path = oldname.substring(0, oldname.lastIndexOf("/") + 1);
            copyFile(currentUser, currentProject, oldname, path + newname);
            deleteFile(currentUser, currentProject, oldname);
        } else {
            throw new CmsException("[" + this.getClass().getName() + "] " + oldname, CmsException.C_NO_ACCESS);
        }
    }

new version:
    /**
     * Renames the file to a new name. <br>
     *
     * Rename can only be done in an offline project. To rename a file, the following
     * steps have to be done:
     * <ul>
     * <li> Copy the file with the oldname to a file with the new name, the state
     * of the new file is set to NEW (2).
     * <ul>
     * <li> If the state of the original file is UNCHANGED (0), the file content of the
     * file is read from the online project. </li>
     * <li> If the state of the original file is CHANGED (1) or NEW (2) the file content
     * of the file is read from the offline project. </li>
     * </ul>
     * </li>
     * <li> Set the state of the old file to DELETED (3). </li>
     * </ul>
     *
     * <B>Security:</B>
     * Access is granted, if:
     * <ul>
     * <li>the user has access to the project</li>
     * <li>the user can write the resource</li>
     * <li>the resource is locked by the callingUser</li>
     * </ul>
     *
     * @param currentUser The user who requested this method.
     * @param currentGroup The current group of the user.
     * @param currentProject The current project of the user.
     * @param oldname The complete path to the resource which will be renamed.
     * @param newname The new name of the resource (CmsUser callingUser, No path information allowed).
     *
     * @throws CmsException  Throws CmsException if operation was not succesful.
     */
    public void renameFile(CmsUser currentUser, CmsGroup currentGroup, CmsProject currentProject, String oldname, String newname) throws CmsException {
    
        // read the old file
        CmsResource file = readFileHeader(currentUser, currentProject, oldname);
    
        // checks, if the newname is valid, if not it throws a exception
        validFilename(newname);
    
        // has the user write-access?
        if (accessWrite(currentUser, currentProject, file)) {
            String path = oldname.substring(0, oldname.lastIndexOf("/") + 1);
            copyFile(currentUser, currentGroup, currentProject, oldname, path + newname);
            deleteFile(currentUser, currentProject, oldname);
        } else {
            throw new CmsException("[" + this.getClass().getName() + "] " + oldname, CmsException.C_NO_ACCESS);
        }
    }

com.opencms.file.I_CmsResourceBroker
- add parameter currentGroup to the following methods
new version:
    public void copyFile(CmsUser currentUser, CmsGroup currentGroup, CmsProject currentProject,
                         String source, String destination)
        throws CmsException;
    public void copyFolder(CmsUser currentUser, CmsGroup currentGroup, CmsProject currentProject,
                         String source, String destination)
        throws CmsException;
    public void moveFile(CmsUser currentUser, CmsGroup currentGroup, CmsProject currentProject,
                         String source, String destination)
        throws CmsException;
    public void renameFile(CmsUser currentUser, CmsGroup currentGroup, CmsProject currentProject,
                           String oldname, String newname)
        throws CmsException;

com.opencms.file.CmsObject
- add parameter currentGroup to broker call in the following methods (signature of CmsObject methods remains unchanged)
new version:
    protected void doCopyFile(String source, String destination) throws CmsException {
        m_rb.copyFile(m_context.currentUser(), m_context.currentGroup(), m_context.currentProject(), getSiteRoot(source), getSiteRoot(destination));
    }
    protected void doCopyFolder(String source, String destination) throws CmsException {
        m_rb.copyFolder(m_context.currentUser(), m_context.currentGroup(), m_context.currentProject(), getSiteRoot(source), getSiteRoot(destination));
    }
    protected void doMoveFile(String source, String destination) throws CmsException {
        m_rb.moveFile(m_context.currentUser(), m_context.currentGroup(), m_context.currentProject(), getSiteRoot(source), getSiteRoot(destination));
    }
    protected void doRenameFile(String oldname, String newname) throws CmsException {
        m_rb.renameFile(m_context.currentUser(), m_context.currentGroup(), m_context.currentProject(), getSiteRoot(oldname), newname);
    }

---------------------------------------------------------------------------------------------------

Fix for Bug 348: CmsResourceBroker.readAllFileHeadersForHist throws a CmsException if the 
resource has been deleted. The method calls readFileHeader to check read access 
to the resource. This fails if the resource has already been deleted. A method 
called "forHist" should only access backup resources as they still exists even 
for deleted files.

As other forHist methods also do not have an access check, we removed the check here.

com.opencms.file.genericSql.CmsResourceBroker
- change method readAllFileHeadersForHist
old version:
     public Vector readAllFileHeadersForHist(CmsUser currentUser, CmsProject currentProject,
                                      String filename)
         throws CmsException {
         CmsResource cmsFile = readFileHeader(currentUser,currentProject, filename);
         if( accessRead(currentUser, currentProject, cmsFile) ) {

            // access to all subfolders was granted - return the file-history.
            return(m_dbAccess.readAllFileHeadersForHist(filename));
        } else {
            throw new CmsException("[" + this.getClass().getName() + "] " + filename,
                 CmsException.C_ACCESS_DENIED);
        }
     }

new version:
     public Vector readAllFileHeadersForHist(CmsUser currentUser, CmsProject currentProject,
                                      String filename)
         throws CmsException {

         return(m_dbAccess.readAllFileHeadersForHist(filename));
     }
     
---------------------------------------------------------------------------------------------------

Two different backup versions of a resource are considered equal. CmsBackupResource.equals() should
not only check the file name but also the version id.

com.opencms.file.CmsBackupResource:
- add method equals
    /**
     * Compares the overgiven object with this object.
     * Two CmsBackupResources are considered equal if they have the same absolute resource name
     * and the same version id.
     * @return true, if the object is identically else it returns false.
     */
    public boolean equals(Object obj) {
        boolean equal;
        if (obj instanceof CmsBackupResource) {
            // parent class checks only resource name, not enough for backups
            equal=super.equals(obj);
            if (equal)  {
                // same version ID than the current resource?
                if (((CmsBackupResource)obj).getVersionId() != m_versionId){
                    equal = false;
                }
            }
        } else {
            equal = false;
        }
        
        return equal;
    }

---------------------------------------------------------------------------------------------------

Bug 364:
Trying to send a mail from a scheduled job fails with NullPointerException.
Reason: CmsMail always tries to get additional attachments from request, that is not available in a scheduled job.

com.opencms.defaults.CmsMail
- change method buildMessage
old version:
        // Set content and attachments
        Vector v = new Vector();
        if (c_CMS != null){
            Enumeration enum = c_CMS.getRequestContext().getRequest().getFileNames();
            while(enum.hasMoreElements()) {
                v.addElement(enum.nextElement());
            }
        }

new version:
        // Set content and attachments
        Vector v = new Vector();
        if (c_CMS != null && c_CMS.getRequestContext() != null && c_CMS.getRequestContext().getRequest() != null){
            Enumeration enum = c_CMS.getRequestContext().getRequest().getFileNames();
            while(enum.hasMoreElements()) {
                v.addElement(enum.nextElement());
            }
        }

---------------------------------------------------------------------------------------------------

Bug 365
Access checks for read and visible return a wrong result if the user has these privileges via his group.

com.opencms.file.genericSql.CmsResourceBroker
- change method accessReadVisible
1st change, old version:
        if ((resource == null) || !accessProject(currentUser, currentProject, resource.getProjectId()) ||
            (!accessOther(resource, C_ACCESS_PUBLIC_READ + C_ACCESS_PUBLIC_VISIBLE) &&
             !accessOwner(currentUser, currentProject, resource, C_ACCESS_PUBLIC_READ + C_ACCESS_PUBLIC_VISIBLE) &&
             !accessGroup(currentUser, currentProject, resource, C_ACCESS_PUBLIC_READ + C_ACCESS_PUBLIC_VISIBLE))) {
            return false;
        }
1st change, new version:
        if ((resource == null) || !accessProject(currentUser, currentProject, resource.getProjectId()) ||
            (!accessOther(resource, C_ACCESS_PUBLIC_READ + C_ACCESS_PUBLIC_VISIBLE) &&
             !accessOwner(currentUser, currentProject, resource, C_ACCESS_OWNER_READ + C_ACCESS_OWNER_VISIBLE) &&
             !accessGroup(currentUser, currentProject, resource, C_ACCESS_GROUP_READ + C_ACCESS_GROUP_VISIBLE))) {
            return false;
        }

2nd change, old version:
            if (!accessOther(res, C_ACCESS_PUBLIC_READ + C_ACCESS_PUBLIC_VISIBLE) &&
                !accessOwner(currentUser, currentProject, res, C_ACCESS_PUBLIC_READ + C_ACCESS_PUBLIC_VISIBLE) &&
                !accessGroup(currentUser, currentProject, res, C_ACCESS_PUBLIC_READ + C_ACCESS_PUBLIC_VISIBLE)) {
                return false;
            }
2nd change, new version:
            if (!accessOther(res, C_ACCESS_PUBLIC_READ + C_ACCESS_PUBLIC_VISIBLE) &&
                !accessOwner(currentUser, currentProject, res, C_ACCESS_OWNER_READ + C_ACCESS_OWNER_VISIBLE) &&
                !accessGroup(currentUser, currentProject, res, C_ACCESS_GROUP_READ + C_ACCESS_GROUP_VISIBLE)) {
                return false;
            }

---------------------------------------------------------------------------------------------------

-------------- next part --------------
Enhancements for OpenCms 5.0.1:
-------------------------------

This file lists the enhancements made by GETIT GmbH for productional use of OpenCms 5.0.1.

---------------------------------------------------------------------------------------------------

Change the group for a resource recursively from CmsShell:

com.opencms.coreCmsShellCommands.java, new method:
	/**
	 * Changes the group for this resource recursively<BR/>
	 *
	 * The user may change this, if he is admin of the resource.
	 *
	 * @param filename The complete path to the resource.
	 * @param newGroup The new of the new group for this resource.
	 * @param recursively shows if the subResources (of a folder) should be changed too.
	 */
	public void chgrp(String filename, String newGroup, String recursively) {
		try {
			m_cms.chgrp(filename, newGroup, 
				recursively.toLowerCase().equals("true"));
		}
		catch(Exception exc) {
			CmsShell.printException(exc);
		}
	}

---------------------------------------------------------------------------------------------------

Methods to access a project with a specific name from CmsShell
In 5.0.1, you can also change to a project if you have its project id. If you create a project
using CmsShell, you specify its name but you do not know its id. So you cannot do anything with this 
project later. We added some methods using the name as parameter.

com.opencms.coreCmsShellCommands.java, new methods:
	/**
	 * Sets the current project for the user.
	 *
	 * @param name The name of the project to be set as current.
	 *             There should be only one project with this name,
	 * 			   otherwise this call sets the first project found.
	 */
	public void setCurrentProjectWithName(String name) {
		try {
			
			Vector projects = m_cms.getAllAccessibleProjects();
			for(int i = 0;i < projects.size();i++) {
				CmsProject project = (CmsProject)projects.elementAt(i);
				if (project.getName().equals(name)) {
					System.out.println(m_cms.getRequestContext()
						.setCurrentProject(project.getId()));
					break;
				}
			}			
		}
		catch(Exception exc) {
			CmsShell.printException(exc);
		}
	}

	/**
	 * Deletes all projects with a specified name.
	 *
	* @param name The name of the project to be deleted.
	*             There should be only one project with this name,
	* 			  otherwise this call deletes all projects found!
	*/
	public void deleteProjectWithName(String name) {
		try {
			Vector projects = m_cms.getAllAccessibleProjects();
			for(int i = 0;i < projects.size();i++) {
				CmsProject project = (CmsProject)projects.elementAt(i);
				if (project.getName().equals(name)) {
					m_cms.deleteProject(project.getId());
				}
			}
		}
		catch(Exception exc) {
			CmsShell.printException(exc);
		}
	}


---------------------------------------------------------------------------------------------------

Methods to read properties from a backup version for a resource:

com.opencms.file.genericSql.CmsDbAccess:
- new method:
    /**
     * Returns a list of all history properties of a file or folder.<p>
     *
     * @param resourceId the id of the resource
     * @param resource the resource to read the properties from
     * @param resourceType the type of the resource
     *
     * @return a Map of Strings representing the properties of the resource
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public HashMap readPropertiesForHist(CmsResource resource, int resourceType)
        throws CmsException {

        HashMap returnValue = new HashMap();
        ResultSet result = null;
        PreparedStatement statement = null;
        Connection con = null;
        String usedPool;
        int resourceId = resource.getResourceId();
        //int onlineProject = getOnlineProject(projectId).getId();
        usedPool = m_poolNameBackup;
        try {
           con = DriverManager.getConnection(usedPool);
            // create project
            statement = con.prepareStatement(m_cq.get("C_PROPERTIES_READALL_BACKUP"));
            statement.setInt(1, resourceId);
            statement.setInt(2, resourceType);
            result = statement.executeQuery();
            while(result.next()) {
                 returnValue.put(result.getString(m_cq.get("C_PROPERTYDEF_NAME")),
                                 result.getString(m_cq.get("C_PROPERTY_VALUE")));
            }
        } catch( SQLException exc ) {
            throw new CmsException("[" + this.getClass().getName() + "] " + exc.getMessage(),
                CmsException.C_SQL_ERROR, exc);
        } finally {
            // close all db-resources
            if(result != null) {
                try {
                    result.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
            if(statement != null) {
                try {
                    statement.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
            if(con != null) {
                try {
                    con.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
        }
        return(returnValue);
    }

com.opencms.file.genericSql.CmsResourceBroker:
- new constant:
    protected static final String C_CACHE_ALL_BACKUP_PROPERTIES = "__CACHE_ALL_BACKUP_PROPERTIES__";

- new method:
    /**
     * Looks up all properties for a resource with optional direcory upward cascading.<p>
     * 
     * 
     * @param currentUser the current user
     * @param currentProject the current project of the user
     * @param resource the resource to look up the property for
     * @param siteRoot the current site root
     * @param search if <code>true</code>, the properties will also be looked up on all parent folders
     *   and the results will be merged, if <code>false</code> not (ie. normal property lookup)
     * @return Map of Strings representing all properties of the resource
     * @throws CmsException in case there where problems reading the properties
     */
    public Map readPropertiesForHist(CmsUser currentUser, CmsProject currentProject, String resource, String siteRoot, boolean search, int version_id) 
    throws CmsException {
        // read the resource from history
        CmsResource res = readFileForHist (currentUser, currentProject, version_id,resource);
        
        search = search && (siteRoot != null);
        // check if we have the result already cached
        HashMap value = null;
        
        String cacheKey = getCacheKey(C_CACHE_ALL_BACKUP_PROPERTIES + search+version_id, null, new CmsProject(currentProject.getId(), -1), res.getResourceName());
        value = (HashMap)m_propertyCache.get(cacheKey);
        
        if (value == null) {
            // result not cached, let's look it up in the DB
            if (search) {
                boolean cont;
                siteRoot += "/";
                value = new HashMap();
                HashMap parentValue;
                do {
                    parentValue = (HashMap)readProperties(currentUser, currentProject, resource, siteRoot, false);
                    parentValue.putAll(value);
                    value.clear();
                    value.putAll(parentValue);
                    resource = CmsResource.getParent(resource);
                    cont = (! siteRoot.equals(resource));
                } while (cont);
            } else {
                value = m_dbAccess.readPropertiesForHist(res, res.getType());
            }  
            // store the result in the cache
            m_propertyCache.put(cacheKey, value);
        }
        return (Map)value.clone();
    }    

com.opencms.file.I_CmsResourceBroker:
- new method:
    /**
     * Looks up all properties from ab backup resource with optional direcory upward cascading.<p>
     * 
     * <b>Security:</b>
     * Only a user is granted who has the right to read the resource.
     * 
     * @param currentUser the current user
     * @param currentProject the current project of the user
     * @param resource the resource to look up the property for
     * @param siteroot the site root where to stop the cascading
     * @param search if <code>true</code>, the properties will also be looked up on all parent folders
     *   and the results will be merged, if <code>false</code> not (ie. normal property lookup)
     * @param versionId the version id containing the defined properties
     * @return Map of Strings representing all properties of the resource
     * @throws CmsException in case there where problems reading the properties
     */
	public Map readPropertiesForHist(CmsUser currentUser, CmsProject currentProject, String resource, String siteRoot, boolean search, int versionId) throws CmsException;    

com.opencms.file.CmsObject:
- new method:
    /**
     * Looks up all properties for a resource.<p>
     * 
     * @param resource the resource to look up the property for
     * @param versionId the requeste version of the resource
     * @return Map of Strings representing all properties of the resource
     * @throws CmsException in case there where problems reading the properties
     */  
    public Map readProperties(String resource,int versionId) throws CmsException {
    	return m_rb.readPropertiesForHist(m_context.currentUser(), m_context.currentProject(), m_context.getSiteRoot(resource), m_context.getSiteRoot(), false,versionId);
    }

---------------------------------------------------------------------------------------------------

CmsSetupUtils cannot read Property values containing a comma. We enhanced the class to read commas when escaped as \,

com.opencms.boot.CmsSetupUtils:
- change method splitMultipleValues:
old version:
    private String splitMultipleValues(String value)  {
      String tempValue = "";
      StringTokenizer st = new StringTokenizer(value,",");
      int counter = 1;
      int max = st.countTokens();
      while(st.hasMoreTokens()) {
        tempValue += st.nextToken().trim();
        if(counter < max)  {
          tempValue += ", \\ \n";
        }
        counter++;
      }
      return tempValue;
    }
new version:
    private String splitMultipleValues(String value)  {
        StringBuffer tmp = new StringBuffer();
        int start = 0;
        int stop = -1;
        while (stop<value.length()) {
            stop = value.indexOf(',',stop+1);
            while (stop>=1 && value.charAt(stop-1)=='\\') {
                stop = value.indexOf(',',stop+1);
            }
            if (stop==-1) stop=value.length();
            tmp.append(value.substring(start,stop).trim());
            if (stop<value.length()) tmp.append(", \\ \n");
            start=stop+1;
        }
        return tmp.toString();
    }
    
---------------------------------------------------------------------------------------------------

Functionality for users to be logged in without a password. Can be used in scheduled jobs.

com.opencms.file.genericSql.CmsResourceBroker:
- new method:

    /**
     * Logs a user into the Cms, if the password is correct.
     *
     * <B>Security</B>
     * All users are granted.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param username The name of the user to be returned.
     * @param password The password of the user to be returned.
     * @return the logged in user.
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public CmsUser loginUser(CmsUser currentUser, CmsProject currentProject,
                               String username)
        throws CmsException {

        // we must read the user from the dbAccess to avoid the cache
        CmsUser newUser = m_dbAccess.readUser(username , C_USER_TYPE_SYSTEMUSER);

        // is the user enabled?
        if( newUser.getFlags() == C_FLAG_ENABLED ) {
            // Yes - log him in!
            // first write the lastlogin-time.
            newUser.setLastlogin(new Date().getTime());
            // write the user back to the cms.
            m_dbAccess.writeUser(newUser);
            // update cache
            m_userCache.put(new CacheId(newUser), newUser);
            return(newUser);
        } else {
            // No Access!
            throw new CmsException("[" + this.getClass().getName() + "] " + username,
                CmsException.C_NO_ACCESS );
        }
    }    
    
com.opencms.file.I_CmsResourceBroker:
- new method:
    /**
     * Logs a user into the Cms, no password is needed.
     *
     * <B>Security</B>
     * All users are granted.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param username The name of the user to be returned.
     * @return the logged in user.
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public CmsUser loginUser(CmsUser currentUser, CmsProject currentProject,
                               String username)
        throws CmsException;        

com.opencms.file.CmsObject:
- new method:
/**
 * Logs a user into the Cms withou providing a password
 *
 * @param username the name of the user.
 * @return the name of the logged in user.
 *
 * @throws CmsException if operation was not successful
 */
public String loginUser(String username) throws CmsException {
    // login the user
    CmsUser newUser = m_rb.loginUser(m_context.currentUser(), m_context.currentProject(), username);
    // init the new user
    init(m_rb, m_context.getRequest(), m_context.getResponse(), newUser.getName(), newUser.getDefaultGroup().getName(), C_PROJECT_ONLINE_ID, m_context.isStreaming(), m_context.getElementCache(), m_sessionStorage, m_context.getDirectoryTranslator(), m_context.getFileTranslator());

    this.fireEvent(com.opencms.flex.I_CmsEventListener.EVENT_LOGIN_USER, newUser);

    // return the user-name
    this.fireEvent(com.opencms.flex.I_CmsEventListener.EVENT_LOGIN_USER, newUser);
    return (newUser.getName());
}
        
---------------------------------------------------------------------------------------------------

Workplace Popup for folder tree always starts with root folder. We added a parameter to specify another root folder.

com.opencms.workplace.I_CmsWpConstants:
- new constant:
    /** Parameter for rootfolder  */
    public static final String C_PARA_ROOTFOLDER = "rootfolder";

com.opencms.workplace.CmsFolderTree:
- change method getTree:
old version:
        // get current and root folder
        rootFolder = cms.rootFolder().getAbsolutePath();

new version:
        // get current and root folder
        rootFolder = cms.getRequestContext().getRequest().getParameter(C_PARA_ROOTFOLDER);
        if ((rootFolder == null) || (rootFolder.equals("")))
        	rootFolder = cms.rootFolder().getAbsolutePath();


---------------------------------------------------------------------------------------------------

You cannot get a specific project by its name.

com.opencms.file.CmsObject:
- new method:
    /**
	 * Get project id of the project with the given name
	 * 
	 * @param name     The name of the project to be set as current. There should be
	 *                  only one project with this name, otherwise this call sets the
	 *                  first project found.
     * @return         The ID of the project if found, -1 if not
	 */
	public int getProjectWithName(String name) throws CmsException {
		Vector projects = getAllAccessibleProjects();
		for (int i = 0; i < projects.size(); i++) {
			CmsProject project = (CmsProject) projects.elementAt(i);
			if (project.getName().equals(name)) {
				return project.getId();
			}
		}
		return -1;
	}
    
---------------------------------------------------------------------------------------------------

If you write a property and the property definition does not exist, you should be able to automatically
create the definition. Methods for this only exist in DbAccess unreachable for modules.

com.opencms.file.genericSql.CmsResourceBroker:
- change method writeProperty and add new method with different signature
old version:
    /**
     * Writes a propertyinformation for a file or folder.
     *
     * <B>Security</B>
     * Only the user is granted, who has the right to write the resource.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resource The name of the resource of which the propertyinformation has
     * to be read.
     * @param property The propertydefinition-name of which the propertyinformation has to be set.
     * @param value The value for the propertyinfo to be set.
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public void writeProperty(CmsUser currentUser, CmsProject currentProject,
                                     String resource, String property, String value)
        throws CmsException {

        // read the resource
        CmsResource res = readFileHeader(currentUser,currentProject, resource);

        // check the security
        if( ! accessWrite(currentUser, currentProject, res) ) {
             throw new CmsException("[" + this.getClass().getName() + "] " + resource,
                CmsException.C_NO_ACCESS);
        }

        m_dbAccess.writeProperty(property, currentProject.getId(),value, res,res.getType(), false);
        m_propertyCache.clear();
        // set the file-state to changed
        if(res.isFile()){
            m_dbAccess.writeFileHeader(currentProject, (CmsFile) res, true, currentUser.getId());
            if (res.getState()==C_STATE_UNCHANGED) {
                res.setState(C_STATE_CHANGED);
            }
        } else {
            if (res.getState()==C_STATE_UNCHANGED) {
                res.setState(C_STATE_CHANGED);
            }
            m_dbAccess.writeFolder(currentProject, readFolder(currentUser,currentProject, resource), true, currentUser.getId());
        }
        // update the cache
        this.clearResourceCache(resource, currentProject, currentUser);
    }

new version:
    /**
     * Writes a propertyinformation for a file or folder.
     *
     * <B>Security</B>
     * Only the user is granted, who has the right to write the resource.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resource The name of the resource of which the propertyinformation has
     * to be read.
     * @param property The propertydefinition-name of which the propertyinformation has to be set.
     * @param value The value for the propertyinfo to be set.
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public void writeProperty(CmsUser currentUser, CmsProject currentProject,
                                     String resource, String property, String value)
        throws CmsException {
        writeProperty(currentUser, currentProject, resource, property, value, false);
    }

    /**
     * Writes a propertyinformation for a file or folder.
     *
     * <B>Security</B>
     * Only the user is granted, who has the right to write the resource.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resource The name of the resource of which the propertyinformation has
     * to be read.
     * @param property The propertydefinition-name of which the propertyinformation has to be set.
     * @param value The value for the propertyinfo to be set.
     * @param addDefinition If <code>true</code> then the propertydefinition is added if it not exists
     * 
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public void writeProperty(CmsUser currentUser, CmsProject currentProject,
                                     String resource, String property, String value, boolean addDefinition)
        throws CmsException {

        // read the resource
        CmsResource res = readFileHeader(currentUser,currentProject, resource);

        // check the security
        if( ! accessWrite(currentUser, currentProject, res) ) {
             throw new CmsException("[" + this.getClass().getName() + "] " + resource,
                CmsException.C_NO_ACCESS);
        }

        m_dbAccess.writeProperty(property, currentProject.getId(),value, res,res.getType(), addDefinition);
        m_propertyCache.clear();
        // set the file-state to changed
        if(res.isFile()){
            m_dbAccess.writeFileHeader(currentProject, (CmsFile) res, true, currentUser.getId());
            if (res.getState()==C_STATE_UNCHANGED) {
                res.setState(C_STATE_CHANGED);
            }
        } else {
            if (res.getState()==C_STATE_UNCHANGED) {
                res.setState(C_STATE_CHANGED);
            }
            m_dbAccess.writeFolder(currentProject, readFolder(currentUser,currentProject, resource), true, currentUser.getId());
        }
        // update the cache
        this.clearResourceCache(resource, currentProject, currentUser);
    }

com.opencms.file.I_CmsResourceBroker:
- new method:
    /**
     * Writes a propertyinformation for a file or folder.
     *
     * <B>Security</B>
     * Only the user is granted, who has the right to write the resource.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resource The name of the resource of which the propertyinformation has
     * to be read.
     * @param property The propertydefinition-name of which the propertyinformation has to be set.
     * @param value The value for the propertyinfo to be set.
     * @param addDefinition If <code>true</code> then the propertydefinition is added if it not exists
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public void writeProperty(CmsUser currentUser, CmsProject currentProject,
                                     String resource, String property, String value, boolean addDefinition)
        throws CmsException;

com.opencms.file.CmsObject:
- new method:
    /**
     * Writes a property for a file or folder.
     *
     * @param name the resource-name for which the property will be set.
     * @param property the property-definition name.
     * @param value the value for the property to be set.
     * @param addDefinition If <code>true</code> then the propertydefinition is added if it not exists
     *
     * @throws CmsException if operation was not successful.
     */
    public void writeProperty(String name, String property, String value, boolean addDefinition) throws CmsException {
        m_rb.writeProperty(m_context.currentUser(), m_context.currentProject(), getSiteRoot(name), property, value, addDefinition);
    }

---------------------------------------------------------------------------------------------------

If you write some properties and any of the property definitions does not exist, you should be able to automatically
create the definition. Methods for this only exist in DbAccess unreachable for modules.

com.opencms.file.genericSql.CmsResourceBroker:
- change method writeProperties and add new method with different signature
old version:
    /**
     * Writes a couple of propertyinformation for a file or folder.
     *
     * <B>Security</B>
     * Only the user is granted, who has the right to write the resource.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resource The name of the resource of which the propertyinformation
     * has to be read.
     * @param propertyinfos A Hashtable with propertydefinition- propertyinfo-pairs as strings.
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public void writeProperties(CmsUser currentUser, CmsProject currentProject,
                                      String resource, Map propertyinfos)
        throws CmsException {
        // read the resource


        CmsResource res = readFileHeader(currentUser,currentProject, resource);

        // check the security
        if( ! accessWrite(currentUser, currentProject, res) ) {
             throw new CmsException("[" + this.getClass().getName() + "] " + resource,
                CmsException.C_NO_ACCESS);
        }

        m_dbAccess.writeProperties(propertyinfos, currentProject.getId(), res, res.getType());
        m_propertyCache.clear();
        if (res.getState()==C_STATE_UNCHANGED) {
            res.setState(C_STATE_CHANGED);
        }
        if(res.isFile()){
            m_dbAccess.writeFileHeader(currentProject, (CmsFile) res, false, currentUser.getId());
        } else {
            m_dbAccess.writeFolder(currentProject, readFolder(currentUser,currentProject, resource), false, currentUser.getId());
        }
        // update the cache
        this.clearResourceCache(resource, currentProject, currentUser);
    }

new version:
    /**
     * Writes a couple of propertyinformation for a file or folder.
     *
     * <B>Security</B>
     * Only the user is granted, who has the right to write the resource.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resource The name of the resource of which the propertyinformation
     * has to be read.
     * @param propertyinfos A Hashtable with propertydefinition- propertyinfo-pairs as strings.
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public void writeProperties(CmsUser currentUser, CmsProject currentProject,
                                      String resource, Map propertyinfos)
        throws CmsException {
            writeProperties(currentUser, currentProject, resource, propertyinfos, false);
    }
    
    /**
     * Writes a couple of propertyinformation for a file or folder.
     *
     * <B>Security</B>
     * Only the user is granted, who has the right to write the resource.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resource The name of the resource of which the propertyinformation
     * has to be read.
     * @param propertyinfos A Hashtable with propertydefinition- propertyinfo-pairs as strings.
     * @param addDefinition If <code>true</code> then the propertydefinition is added if it not exists
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public void writeProperties(CmsUser currentUser, CmsProject currentProject,
                                      String resource, Map propertyinfos, boolean addDefinition)
        throws CmsException {
        // read the resource


        CmsResource res = readFileHeader(currentUser,currentProject, resource);

        // check the security
        if( ! accessWrite(currentUser, currentProject, res) ) {
             throw new CmsException("[" + this.getClass().getName() + "] " + resource,
                CmsException.C_NO_ACCESS);
        }

        m_dbAccess.writeProperties(propertyinfos, currentProject.getId(), res, res.getType(), addDefinition);
        m_propertyCache.clear();
        if (res.getState()==C_STATE_UNCHANGED) {
            res.setState(C_STATE_CHANGED);
        }
        if(res.isFile()){
            m_dbAccess.writeFileHeader(currentProject, (CmsFile) res, false, currentUser.getId());
        } else {
            m_dbAccess.writeFolder(currentProject, readFolder(currentUser,currentProject, resource), false, currentUser.getId());
        }
        // update the cache
        this.clearResourceCache(resource, currentProject, currentUser);
    }

com.opencms.file.I_CmsResourceBroker:
- new method:
    /**
     * Writes a couple of propertyinformation for a file or folder.
     *
     * <B>Security</B>
     * Only the user is granted, who has the right to write the resource.
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resource The name of the resource of which the propertyinformation
     * has to be read.
     * @param propertyinfos A Hashtable with propertydefinition- propertyinfo-pairs as strings.
     * @param addDefinition If <code>true</code> then the propertydefinition is added if it not exists
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public void writeProperties(CmsUser currentUser, CmsProject currentProject,
                                      String resource, Map propertyinfos, boolean addDefinition)
        throws CmsException;

com.opencms.file.CmsObject:
- new method:
/**
 * Writes a couple of Properties for a file or folder.
 *
 * @param name the resource-name of which the Property has to be set.
 * @param properties a Hashtable with property-definitions and property values as Strings.
 * @param addDefinition If <code>true</code> then the propertydefinition is added if it not exists
 *
 * @throws CmsException if operation was not successful.
 */
public void writeProperties(String name, Map properties, boolean addDefinition) throws CmsException {
    m_rb.writeProperties(m_context.currentUser(), m_context.currentProject(), getSiteRoot(name), properties, addDefinition);
}


---------------------------------------------------------------------------------------------------

We missed a method to find a folder with a specific name searching only through a part of VFS. As relative folder names are not unique, 
the following method returns only the first folder found.

com.opencms.file.genericSql.query.properties:
- add properties:
C_RESOURCES_FIND_FIRST_FOLDER_1=SELECT CMS_RESOURCES.RESOURCE_NAME \
                     FROM CMS_RESOURCES, CMS_PROJECTRESOURCES \
                     WHERE CMS_RESOURCES.RESOURCE_NAME LIKE '
C_RESOURCES_FIND_FIRST_FOLDER_2=' AND CMS_RESOURCES.RESOURCE_TYPE = 0 \
                     AND CMS_RESOURCES.RESOURCE_NAME LIKE CONCAT(CMS_PROJECTRESOURCES.RESOURCE_NAME,'%') \
                     AND CMS_PROJECTRESOURCES.PROJECT_ID IN (?, 1) \
                     ORDER BY CMS_RESOURCES.RESOURCE_NAME, CMS_PROJECTRESOURCES.PROJECT_ID DESC
C_RESOURCES_FIND_FIRST_FOLDER_1_ONLINE=SELECT CMS_ONLINE_RESOURCES.RESOURCE_NAME \
                        FROM CMS_ONLINE_RESOURCES \
                        WHERE RESOURCE_NAME LIKE '
C_RESOURCES_FIND_FIRST_FOLDER_2_ONLINE=' AND PROJECT_ID = ?


com.opencms.file.genericSql.CmsDbAccess:
- new method:
    /**
     * Searches for a vfs folder with a specific relative name.
     * @param project          The project in which the resource will be used.
     * @param folderName       relative name of a vfs folder
     * @param rootFolderName   absolute vfs name of the folder to search in
     * @return absolute name of the first folder found recursively under rootFolderName which name is folderName
     *         null if no folder found
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public String getFirstFolderWithName(CmsProject project, String folderName, String rootFolderName)
        throws CmsException {

        ResultSet res = null;
        PreparedStatement statement = null;
        Connection con = null;
        String usedPool;
        String usedStatement;
        String resourcename = rootFolderName + "%" + folderName;
        String absolutename = null;
        int onlineProject = I_CmsConstants.C_PROJECT_ONLINE_ID;
        if (project.getId() == onlineProject){
            usedPool = m_poolNameOnline;
            usedStatement = "_ONLINE";
        } else {
            usedPool = m_poolName;
            usedStatement = "";
        }
        try {
            con = DriverManager.getConnection(usedPool);
            // read resource data from database
            statement = con.prepareStatement(m_cq.get("C_RESOURCES_FIND_FIRST_FOLDER_1"+usedStatement)+"/default/vfs"+resourcename+m_cq.get("C_RESOURCES_FIND_FIRST_FOLDER_2"+usedStatement));
            statement.setInt(1,project.getId());
            res = statement.executeQuery();
            // get resource name from first resource in the result
            if(res.next()) {
                absolutename=res.getString(m_cq.get("C_RESOURCES_RESOURCE_NAME"));
                if (absolutename.startsWith("/default/vfs")) {
                    absolutename = absolutename.substring("/default/vfs".length());
                }
            }
        } catch (SQLException e){
            throw new CmsException("["+this.getClass().getName()+"]"+e.getMessage(),CmsException.C_SQL_ERROR, e);
        } catch (Exception ex) {
            throw new CmsException("["+this.getClass().getName()+"]", ex);
        } finally {
            // close all db-resources
            if(res != null) {
                try {
                    res.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
            if(statement != null) {
                try {
                    statement.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
            if(con != null) {
                try {
                    con.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
        }
        return absolutename;
    }

com.opencms.file.genericSql.CmsResourceBroker:
- new method:
    /**
     * Searches for a vfs folder with a specific relative name.
     * @param currentUser      ignored in this method
     * @param currentProject   The project in which the resource will be used.
     * @param folderName       relative name of a vfs folder
     * @param rootFolderName   absolute vfs name of the folder to search in
     * @return absolute name of the first folder found recursively under rootFolderName which name is folderName
     *         null if no folder found
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public String getFirstFolderWithName(CmsUser currentUser, CmsProject currentProject, String folderName, String rootFolderName) throws CmsException {
        return m_dbAccess.getFirstFolderWithName(currentProject, folderName, rootFolderName);
    }
    
com.opencms.file.I_CmsResourceBroker:
- new method:
    /**
     * Searches for a vfs folder with a specific relative name.
     * @param currentUser      ignored in this method
     * @param currentProject   The project in which the resource will be used.
     * @param folderName       relative name of a vfs folder
     * @param rootFolderName   absolute vfs name of the folder to search in
     * @return absolute name of the first folder found recursively under rootFolderName which name is folderName
     *         null if no folder found
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public String getFirstFolderWithName(CmsUser currentUser, CmsProject currentProject, String folderName, String rootFolderName) throws CmsException;

com.opencms.file.CmsObject:
- new method:
    /**
     * Searches for a vfs folder with a specific relative name.
     * @param folderName       relative name of a vfs folder
     * @param rootFolderName   absolute vfs name of the folder to search in
     * @return absolute name of the first folder found recursively under rootFolderName which name is folderName
     *         null if no folder found
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public String getFirstFolderWithName(String folderName, String rootFolderName) throws CmsException {
        return m_rb.getFirstFolderWithName(m_context.currentUser(), m_context.currentProject(), folderName, rootFolderName);
    }

---------------------------------------------------------------------------------------------------

We added a method to find all resources in a folder that have been published after a specific date.
This only works if backup is enabled cause a resource's publish date is only stored in the backup resource.

com.opencms.file.genericSql.query.properties:
- add properties:
# all resources that match a filter and have been published afer a specified date
# only works correct if backup is enabled cause otherwise no publish date is saved
C_SELECT_LAST_PUBLISHED_RESOURCES=SELECT DISTINCT RESOURCE_NAME \
    FROM CMS_BACKUP_RESOURCES \
    WHERE TO_CHAR(DATE_CREATED, 'YYYY-MM-DD HH24:MI:SS') > ? \
    AND RESOURCE_NAME LIKE ?
    ORDER BY RESOURCE_NAME

com.opencms.file.mySql.query.properties:
C_SELECT_LAST_PUBLISHED_RESOURCES=SELECT DISTINCT RESOURCE_NAME \
    FROM CMS_BACKUP_RESOURCES \
    WHERE DATE_CREATED > ? \
    AND RESOURCE_NAME LIKE ?
    ORDER BY RESOURCE_NAME

com.opencms.file.genericSql.CmsDbAccess:
- new imports:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;

- new method:
    /**
     * select the names of all resources that have been published after a specific time
     * and belong to a specific root folder
     *
     * @param startDate a date
     * @param folderName absolute name of a VFS folder
     * @return a collection of all resource names from resources 
     * @author Sven Bonorden, GETIT GmbH
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public Vector getLastPublishedResources(Date startDate, String folderName) throws CmsException {
        if (startDate == null) {
           GregorianCalendar cal = new GregorianCalendar(1970,1,1);
           startDate = new Date(cal.getTimeInMillis());
        }
        String startDateString = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(startDate);
        
        if (folderName == null || folderName.length()==0) {
           folderName = "";
        }
        if (!folderName.startsWith("/default/vfs")) {
            folderName = "/default/vfs" + folderName;
        }
        
        Connection con = null;
        PreparedStatement statement = null;
        ResultSet res = null;
        Vector publishedResources = new Vector();
        try {
            con = DriverManager.getConnection(m_poolNameBackup);
            statement = con.prepareStatement(m_cq.get("C_SELECT_LAST_PUBLISHED_RESOURCES"));

            statement.setString(1, startDateString);
            statement.setString(2, folderName + "%");
            
            res = statement.executeQuery();
            while (res.next()) {
                publishedResources.addElement(res.getString("RESOURCE_NAME"));
            }
            res.close();
        } catch (SQLException e) {
            throw new CmsException("[" + this.getClass().getName() + "] " + e.getMessage(), CmsException.C_SQL_ERROR, e);
        } finally {
            if (statement != null) {
                try{
                    statement.close();
                } catch (SQLException e){
                }
            }
            if (con != null) {
                try{
                    con.close();
                } catch (SQLException e){
                }
            }
        }
        return publishedResources;
    }

com.opencms.file.genericSql.CmsResourceBroker:
- new method:
    /**
     * select the names of all resources that have been published after a specific time
     * and belong to a specific root folder
     *
     * @param startDate a date
     * @param folderName absolute name of a VFS folder
     * @return a collection of all resource names from resources 
     * @author Sven Bonorden, GETIT GmbH
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public Vector getLastPublishedResources(Date startDate, String folderName) throws CmsException {
        return m_dbAccess.getLastPublishedResources(startDate, folderName);
    }

com.opencms.file.I_CmsResourceBroker:
- new method:
    /**
     * select the names of all resources that have been published after a specific time
     * and belong to a specific root folder
     *
     * @param startDate a date
     * @param folderName absolute name of a VFS folder
     * @return a collection of all resource names from resources 
     * @author Sven Bonorden, GETIT GmbH
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public Vector getLastPublishedResources(Date startDate, String folderName) 
        throws CmsException;

com.opencms.file.CmsObject:
- new method:
    /**
     * Select the names of all resources that have been published after a specific time
     * and belong to a specific root folder.
     * This method ignores access priviledges and projects, all published files are returned. 
     *
     * @param startDate a date
     * @param folderName absolute name of a VFS folder
     * @return a collection of all resource names from resources 
     * @author Sven Bonorden, GETIT GmbH
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public Vector getLastPublishedResources(Date startDate, String folderName) throws CmsException {
        return m_rb.getLastPublishedResources(startDate, folderName);
    }

---------------------------------------------------------------------------------------------------

It was impossible to get the resource type of a deleted resource (that is still available in the backup). 

com.opencms.file.genericSql.query.properties:
- add properties:
C_RESOURCES_READ_ALL_BACKUP_ORDER_DESC=SELECT CMS_BACKUP_RESOURCES.*, CMS_BACKUP_RESOURCES.PROJECT_ID LOCKED_IN_PROJECT \
                            FROM CMS_BACKUP_RESOURCES WHERE RESOURCE_NAME=?  \
                            ORDER BY VERSION_ID DESC

com.opencms.file.genericSql.CmsDbAccess:
- new method:
    /**
     * Reads the resource type of a historic file in the OpenCms.<BR>
     * 
     * @param resourceName The name of the file to be read.
     * @return resource type of the last backed-up version of this resource.
     * @throws CmsException Throws CmsException if operation was not succesful
     * @author Sven Bonorden, GETIT GmbH
     */
    public int getResourceTypeForHist(String resourceName)
        throws CmsException {

        int resourceType = -1;
        ResultSet res =null;
        PreparedStatement statement = null;
        Connection con = null;

        try {
            con = DriverManager.getConnection(m_poolNameBackup);
            statement = con.prepareStatement(m_cq.get("C_RESOURCES_READ_ALL_BACKUP_ORDER_DESC"));
            // read file header data from database
            statement.setString(1, resourceName);
            res = statement.executeQuery();
            // read only the first row because this is the last version of the resource 
            if(res.next()) {
                resourceType= res.getInt(m_cq.get("C_RESOURCES_RESOURCE_TYPE"));
            }
        } catch (SQLException e){
            throw new CmsException("["+this.getClass().getName()+"] "+e.getMessage(),CmsException.C_SQL_ERROR, e);
        } catch( Exception exc ) {
            throw new CmsException("readResourceTypeForHist "+exc.getMessage(), CmsException.C_UNKNOWN_EXCEPTION, exc);
        } finally {
            // close all db-resources
            if(res != null) {
                try {
                    res.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
            if(statement != null) {
                try {
                    statement.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
            if(con != null) {
                try {
                    con.close();
                } catch(SQLException exc) {
                    // nothing to do here
                }
            }
        }
        return resourceType;
    }

com.opencms.file.genericSql.CmsResourceBroker:
- new method:
    /**
     * Reads the resource type of a historic file in the OpenCms.<BR>
     * No security checks.
     * 
     * @author Sven Bonorden, GETIT GmbH
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resourceName The name of the file to be read.
     *
     * @return resource type of the last backed-up version of this resource.
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public int getResourceTypeForHist(CmsUser currentUser, CmsProject currentProject, String resourceName)
        throws CmsException {
        // no security check, accessRead cannot be used if the file was deleted.

        return(m_dbAccess.getResourceTypeForHist(resourceName));        
    }

com.opencms.file.I_CmsResourceBroker:
- new method:
    /**
     * Reads the resource type of a historic file in the OpenCms.<BR>
     * No security checks.
     * 
     * @author Sven Bonorden, GETIT GmbH
     *
     * @param currentUser The user who requested this method.
     * @param currentProject The current project of the user.
     * @param resourceName The name of the file to be read.
     *
     * @return resource type of the last backed-up version of this resource.
     *
     * @throws CmsException Throws CmsException if operation was not succesful
     */
    public int getResourceTypeForHist(CmsUser currentUser, CmsProject currentProject, String resourceName)
        throws CmsException;

com.opencms.file.CmsObject:
- new method:
/**
 * Reads the resource type of a historic file in the OpenCms.<BR>
 * No security checks as this method must also work if the file has been deleted.
 * 
 * @author Sven Bonorden, GETIT GmbH
 *
 * @param resourceName The name of the file to be read.
 * @return resource type of the last backed-up version of this resource.
 * @throws CmsException Throws CmsException if operation was not succesful
 */
public int getResourceTypeForHist(String resourceName) throws CmsException {
    return (m_rb.getResourceTypeForHist(m_context.currentUser(), m_context.currentProject(), getSiteRoot(resourceName)));
}

---------------------------------------------------------------------------------------------------

We enhanced the folder tree popup to show not only the name, but also the title of the resource (if it is different from the name).
This was necessary because our customer needed many folders named by internal ids and only the title showed readable information.
As this change leads to slower performance, it must be checked if useful for other projects.

com.opencms.workplace.CmsFolderTree:
- change method showTree:
old version:
                template.setData(C_TREEENTRY, res.getName());

new version:
                String name = res.getName();
                String title = cms.readProperty(res.getAbsolutePath(), C_PROPERTY_TITLE);
                if (title != null && title.trim().length() > 0 && !title.equalsIgnoreCase(name)) {
                    name = name + " (" + title + ")";
                }
                template.setData(C_TREEENTRY, name);


---------------------------------------------------------------------------------------------------



More information about the opencms-dev mailing list