Changeset 31537

Show
Ignore:
Timestamp:
24.03.2017 21:37:12 (2 years ago)
Author:
ak19
Message:

First commit for getting user comments working for GS3. It all works, but there's debugging statements still and haven't cleaned up commented out code. After that, would like to make POST rather than GET AJAX calls, so more refactoring required. 1. config_format.xsl is just minor spelling corrections. 2. header.xsl has a new function generateLogoutURL. 3. document.xsl imports and calls new usercomments.xsl to set up the user comments area. 4. New usercomments.js is imported by new usercomments.xsl, and sets up the interaction of the usercomments area. 5. javascript-global-functions.js now contains setMetadataArray and getMetadataArray functions to parallel what GS2 used in gsajaxapi.js, but for GS3 need to go through GS2Construct.processModifyMetadata() service in java code. 5. GS2Construct.java does different checking for users adding user comments versus users doing document editing. For the latter, the user needs to have editing permissions for the document. But any user is allowed to add comments on any accessible document. But ModifyMetadata? should not allow any other metadata to be modified other than meta fields. 6. New language strings for usercomment area and GS2Construct errors in the 2 changed properties files.

Location:
main/trunk/greenstone3
Files:
2 added
8 modified

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Construct.java

    r31127 r31537  
    2929import java.util.Map; 
    3030import java.util.Set; 
     31import java.util.regex.Matcher; 
     32import java.util.regex.Pattern; 
    3133 
    3234import org.apache.log4j.Logger; 
     
    4244import org.greenstone.gsdl3.util.UserContext; 
    4345import org.greenstone.gsdl3.util.XMLConverter; 
     46 
     47// https://developer.android.com/reference/org/json/JSONObject.html 
     48// https://developer.android.com/reference/org/json/JSONArray.html 
     49import org.json.JSONArray; 
     50import org.json.JSONException; 
     51import org.json.JSONObject; 
     52 
    4453 
    4554import org.w3c.dom.Document; 
     
    311320    protected Element processModifyMetadata(Element request) 
    312321    { 
    313         if (!userHasCollectionEditPermissions(request)) { 
     322 
     323        // There are two types of operations whereby metadata gets modified: 
     324        // - document including document-meta editing: user needs document editing powers 
     325        // - adding user comments: user just needs an account and needs to be logged in 
     326        // We handle both cases in this service. 
     327 
     328        Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER); 
     329        HashMap<String, Serializable> params = GSXML.extractParams(param_list, false); 
     330 
     331        // If a user is only adding comments, they don't need to have editing powers over a collection 
     332        // but they need to be logged in 
     333        String[] docids = isAddingUserComments(request, params); 
     334        boolean isAddingUserComments = (docids == null) ? false : true; 
     335 
     336        if(isAddingUserComments) { // adding user comments, check if user logged in 
     337        UserContext context = new UserContext(request); 
     338 
     339        // A restricted set of metadata is modifiable when adding user comments: 
     340        // only the username, usertimestamp and usercomment metadata fields. 
     341        // If that's all that's being modified, isAddingUserComments() would have returned true, 
     342        // so finally check if the caller is logged in as a user. 
     343        if (context.getUsername().equals("")) {  
     344            Document result_doc = XMLConverter.newDOM(); 
     345            Element result = GSXML.createBasicResponse(result_doc, "processModifyMetadata"); 
     346            GSXML.addError(result, "Cannot add user comments if not logged in."); // or if attempting to set meta not related to user comments. 
     347            return result; // not logged in 
     348        } 
     349 
     350        } 
     351        else if (!userHasCollectionEditPermissions(request, params)) { 
    314352        Document result_doc = XMLConverter.newDOM(); 
    315353        Element result = GSXML.createBasicResponse(result_doc, "processModifyMetadata"); 
    316         GSXML.addError(result, "This user does not have the required permissions to perform this action."); 
     354        GSXML.addError(result, "This user does not have the required permissions to perform this action."); // also get here if user was attempting to set meta not related to user comments. 
    317355        return result; 
    318356        } 
     
    323361         
    324362        // process 
    325         Element response = runCommand(request, GS2PerlConstructor.MODIFY_METADATA_SERVER); 
     363        Element response = runCommand(request, GS2PerlConstructor.MODIFY_METADATA_SERVER, docids); 
    326364         
    327365        if (response.getElementsByTagName(GSXML.ERROR_ELEM).getLength() <= 0) // if no errors, wait for process to finish 
     
    666704    } 
    667705 
     706    protected Element runCommand(Element request, int type) { 
     707        return runCommand(request, type, null); 
     708    } 
     709 
    668710    /** returns a response element */ 
    669     protected Element runCommand(Element request, int type) 
    670     { 
    671       Document result_doc = XMLConverter.newDOM(); 
     711    protected Element runCommand(Element request, int type, String[] docids) 
     712    { 
     713        Document result_doc = XMLConverter.newDOM(); 
    672714        // the response to send back 
    673715        String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT)); 
     
    757799             
    758800            String oid = null; 
    759              
     801 
    760802            while (i.hasNext()) { 
    761803 
    762804                Map.Entry<String, Serializable> entry = i.next(); 
    763805                String paramname = entry.getKey(); 
    764                 paramname = paramname.replace("s1.", ""); // replaces all 
    765                                                             // occurrences 
     806                paramname = paramname.replace("s1.", ""); // replaces all occurrences 
    766807                if (paramname.equals("collection")) { 
    767808                    paramname = "c"; 
     
    770811                    oid = (String) entry.getValue(); 
    771812                } 
     813                 
    772814                String paramvalue = (String) entry.getValue(); 
    773815 
     
    778820            } 
    779821             
    780             markDocumentInFlatDatabase("R", coll_name, OID.getTop(oid)); 
     822            if(oid != null) { // if we have only one oid 
     823            markDocumentInFlatDatabase("R", coll_name, OID.getTop(oid)); 
     824            } else if (docids != null) { // check if we are dealing with many doc ids, as cold in theory happen when set-metadata-array is called 
     825             
     826            for(int index = 0; index < docids.length; index++) { 
     827                String docid = docids[index]; 
     828                markDocumentInFlatDatabase("R", coll_name, OID.getTop(docid)); 
     829            } 
     830            } else { 
     831            String msg = getTextString("general.no_valid_docid_error", lang); 
     832            logger.error("*** " + msg); 
     833            Text t = result_doc.createTextNode(msg); 
     834            status.appendChild(t); 
     835            status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR)); 
     836            return response; 
     837            } 
    781838             
    782839            constructor.setQueryString(querystring.toString()); 
     
    9651022 
    9661023 
     1024    protected String[] isAddingUserComments(Element request, HashMap<String, Serializable> params) { 
     1025 
     1026    String metaserver_command = (String) params.get("a"); // e.g. set-archives-metadata or set-metadata-array 
     1027    // quickest test: 
     1028    // if not calling set-metadata-array, then it definitely won't be a set-usercomments operation 
     1029    if(!metaserver_command.equals("set-metadata-array")) { 
     1030        return null; 
     1031    } 
     1032 
     1033    // Confirm that the set-meta-array operation is only attempting to modify user comments metadata 
     1034 
     1035    String[] docids = null; 
     1036    String json_str = (String) params.get("json"); // will have a "json" field if doing set-meta-array 
     1037    Pattern p = Pattern.compile("^(username|usertimestamp|usercomment)$"); 
     1038 
     1039    // check that the name of each that's metadata to be set is one of username|usercomment|usertimestamp 
     1040    // Anything else means something more than adding user comments is being attempted, which is invalid 
     1041    try { 
     1042         
     1043        JSONArray docInfoList = new JSONArray(json_str); 
     1044        docids = new String[docInfoList.length()]; 
     1045        for(int index = 0; index < docInfoList.length(); index++) { 
     1046        JSONObject docInfo = docInfoList.getJSONObject(index); 
     1047        if(docInfo.has("metatable")) { // should exist for metadata arrays 
     1048             
     1049            docids[index] = docInfo.getString("docid"); // should exist if metatable existed 
     1050 
     1051            logger.info("@@@ Found docid: " + docids[index]); 
     1052 
     1053            JSONArray metatable = docInfo.getJSONArray("metatable"); 
     1054            for(int i = 0; i < metatable.length(); i++) { 
     1055            JSONObject meta = metatable.getJSONObject(i); 
     1056 
     1057            String metaname = meta.getString("metaname"); 
     1058            logger.info("### metaname: " + metaname); 
     1059            Matcher m = p.matcher(metaname); 
     1060            if(!m.matches()) { 
     1061                logger.info("### metaname: " + metaname + " doesn't match"); 
     1062                return null; 
     1063            } 
     1064            } 
     1065        } 
     1066        } 
     1067    } catch(JSONException jsonex) { 
     1068        logger.error("Exception when parsing json string: " + json_str); 
     1069        logger.error(jsonex); 
     1070         
     1071    } 
     1072     
     1073    // if we're here, then it means that the JSON only asked for username|usercomment|usertimestamp meta 
     1074    // meaning that the setmeta operation was a valid user comment operation. 
     1075    // In that case, we have a docid for which we need to add a user comment  
     1076    // set-metadata-array can take more docids, but doesn't happen for a user comment. And one comment 
     1077    // is added at a time, but 3 meta fields are set for each comment: username, usercomment and timestamp 
     1078    // hence the use of set-meta-array. 
     1079    return docids; 
     1080 
     1081    } 
     1082 
    9671083    /** Copy from DebugService.userHasEditPermissions 
    9681084     This function checks that the user is logged in and that the user  
     
    9711087    Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER); 
    9721088    HashMap<String, Serializable> params = GSXML.extractParams(param_list, false); 
     1089 
     1090    return userHasCollectionEditPermissions(request, params); 
     1091 
     1092    } 
     1093 
     1094    protected boolean userHasCollectionEditPermissions(Element request, HashMap<String, Serializable> params) { 
    9731095    String collection = (String) params.get(COL_PARAM); // could be null on newcoll operation 
     1096 
    9741097 
    9751098    UserContext context = new UserContext(request); 
     
    9771100    return !context.getUsername().equals(""); 
    9781101    } 
     1102 
     1103    // START DEBUG 
     1104    Set<Map.Entry<String, Serializable>> entries = params.entrySet(); 
     1105    Iterator<Map.Entry<String, Serializable>> i = entries.iterator(); 
     1106     
     1107    StringBuffer parametersLine = new StringBuffer(); 
     1108    while (i.hasNext()) { 
     1109     
     1110    Map.Entry<String, Serializable> entry = i.next(); 
     1111    String paramname = entry.getKey(); 
     1112    String paramvalue = (String) entry.getValue(); 
     1113     
     1114    parametersLine.append("\t" + paramname + ": " + paramvalue + "\n"); 
     1115    } 
     1116     
     1117    logger.info("XXXXXXXXXXXXX PARAMETERS:\n" + parametersLine); 
     1118    // END DEBUG 
     1119 
    9791120    for (String group : context.getGroups()) { 
    9801121      // administrator always has permission 
  • main/trunk/greenstone3/web/WEB-INF/classes/GS2Construct.properties

    r10042 r31537  
    5959general.configure_constructor_error=Couldn't configure the collection constructor. 
    6060general.process_start=Starting process... 
     61general.no_valid_docid_error=Both oid and docids are null. Could happen if adding user comments attempted to set non-usercomment meta. 
    6162 
    6263 
     
    6566 
    6667 
    67  
  • main/trunk/greenstone3/web/WEB-INF/classes/interface_default.properties

    r31381 r31537  
    141141 
    142142doc.map_nearby_docs=Show documents near here 
     143 
    143144maps.scrolllabel=Scroll through places 
    144145 
     
    146147external.text=The link you have selected is external to any of your currently selected collections. If you still wish to view this link and your browser has access to the Web, you can {0-go-forward-link} to this page; otherwise use your browsers 'back' button to return to the previous document. 
    147148external.go_forward=go forward 
     149 
     150######################################################### 
     151#  User Comments section (available on a document page) # 
     152######################################################### 
     153 
     154usercomments.username=User name 
     155usercomments.add=Add Comment 
     156usercomments.submit=Submit Comment 
     157usercomments.submitted=Comment Submitted 
     158usercomments.isempty=Comment was empty. 
     159usercomments.heading=Comments 
    148160 
    149161##################### 
  • main/trunk/greenstone3/web/interfaces/default/js/javascript-global-functions.js

    r31529 r31537  
    422422} 
    423423 
    424 function callMetadataServer(callingFunction, url, responseFunction) 
     424// No function overloading in JavaScript. Can pass a custom object, however, see 
     425// http://stackoverflow.com/questions/456177/function-overloading-in-javascript-best-practices 
     426function callMetadataServer(callingFunction, url, responseFunction, opts) 
    425427{ 
    426428    var async_setting = true; // Internal processing of 'read' operations (get meta) is not order dependent 
     
    445447    } // otherwise, such as for get- metadata operation, we proceed as before, which will not require authentication 
    446448 
     449    if (opts != null && opts["forceSync"] != null) { 
     450    async_setting = (!opts["forceSync"]); 
     451    } 
     452 
     453    console.log("Away to call: " + url); 
     454    var ajaxResponse = null; 
    447455 
    448456    $.ajax(url, {async: async_setting}) 
     
    451459        console.log("(" + callingFunction + ") Response received from server: " + response); 
    452460 
     461        ajaxResponse = response; 
     462 
    453463        //var xml = $.parseXML(response); 
    454464        //console.log(xml); 
     
    456466        if(responseFunction != null) 
    457467        { 
    458             responseFunction(response); 
     468             
     469            responseFunction(response); 
    459470        } 
    460471    }) 
     
    463474        console.log("(" + callingFunction + ") Failed"); 
    464475    }); 
     476 
     477    console.log("Finished ajax call to: " + url); 
     478     
     479    console.log("Got response: " + ajaxResponse); 
     480    return ajaxResponse; 
    465481} 
    466482 
     
    513529} 
    514530 
     531// New. Modified version of the GS2 version of this method in gsajaxapi.js. 
     532// The where parameter can be specified as one or more of: import, archives, index, live  
     533// separated by |. If null, it is assumed to be index which is the original default  
     534// behaviour of calling set-metadata-array. E.g. where=import|archives|index 
     535// THIS METHOD IS SYNCHRONOUS 
     536gs.functions.setMetadataArray = function(collection, site, docArray, metamode, where, responseFunction)  
     537{ 
     538    docArrayJSON = JSON.stringify(docArray); 
     539     
     540    var params = "a=" + escape("set-metadata-array"); //"a=set-metadata-array"; 
     541    if(where != null) { 
     542    params += "&where=" + escape(where); // if where not specified, meta-server will default to setting index meta 
     543    //} else { 
     544    //    params += "&where=import|archives|index"; 
     545    } 
     546    params += "&c="+escape(collection); 
     547    params += "&site="+escape(site); 
     548    params += "&json="+escape(docArrayJSON); 
     549     
     550    if (metamode!=null) { 
     551    params += "&metamode=" + escape(metamode); 
     552    } 
     553     
     554 
     555    var response = callMetadataServer("Setting metadata in "+where, "cgi-bin/metadata-server.pl?"+params, responseFunction); 
     556 
     557    return response; 
     558    // return this.urlPostSync(mdserver,params); // gsajaxapi.js version for GS2 
     559} 
     560 
     561 
    515562/************************* 
    516563* GET METADATA FUNCTIONS * 
    517564*************************/ 
    518565 
     566// New. Modified version of the GS2 version of this method in gsajaxapi.js. 
     567// See description for setMetadataArray above for information about the 'where' parameter. 
     568// THIS METHOD IS SYNCHRONOUS BY DEFAULT. Set forceSync to false to override this default behaviour 
     569gs.functions.getMetadataArray = function(collection, site, docArray, where, forceSync, responseFunction) 
     570{ 
     571    docArrayJSON = JSON.stringify(docArray); 
     572     
     573    var params = "a=" + escape("get-metadata-array"); //"a=set-metadata-array"; 
     574    if(where != null) { 
     575    params += "&where=" + escape(where); // if where not specified, meta-server will default to setting index meta 
     576    //} else { 
     577    //    params += "&where=import|archives|index"; 
     578    } 
     579    params += "&c="+escape(collection); 
     580    params += "&site="+escape(site); 
     581    params += "&json="+escape(docArrayJSON); 
     582         
     583    // get operations are generally asynchronous, but allow calling function to force ajax call  
     584    // to be synchronous or not. Default is synchronous, as it was for GS2 
     585    if(forceSync == null) { 
     586    forceSync = true; 
     587    } 
     588    // Objects/maps can use identifiers or strings for property names 
     589    // http://stackoverflow.com/questions/456177/function-overloading-in-javascript-best-practices 
     590    // https://www.w3schools.com/js/js_objects.asp 
     591    var response = callMetadataServer("Getting metadata from "+where, "cgi-bin/metadata-server.pl?"+params, responseFunction, {"forceSync":forceSync}); 
     592 
     593    return response; 
     594    //return this.urlPostSync(mdserver,params); // gsajaxapi.js version for GS2 
     595} 
     596 
     597 
    519598gs.functions.getImportMetadata = function(collection, site, documentID, metadataName, responseFunction) 
    520599{ 
  • main/trunk/greenstone3/web/interfaces/default/style/core.css

    r31530 r31537  
    12401240} 
    12411241 
     1242/* User comments area: display of existing user comments and display of the form and of the add user/logout links */ 
     1243 
     1244#usercomments, #usercommentform, #usercommentlink a, #usercommentlogoutlink a { 
     1245    font-family: sans-serif; 
     1246    font-size: 12px; 
     1247} 
     1248 
     1249#usercomments { 
     1250    margin: 10px 0; 
     1251} 
     1252 
     1253.usercommentheading { 
     1254    font-weight: bold; 
     1255    color: #006666; 
     1256    border-top: solid 1px black; 
     1257} 
     1258 
     1259.usercomment { 
     1260    margin: 10px 0; 
     1261} 
     1262 
     1263#usercommentlink, #usercommentlogoutlink { 
     1264    margin: 10px 0 20px 0; 
     1265} 
     1266 
     1267#usercommentlink a, #usercommentlogoutlink a { 
     1268 text-decoration: none; 
     1269 font-weight: bold; 
     1270 color: #006666; 
     1271} 
     1272#usercommentlink a:visited, #usercommentlogoutlink a:visited { 
     1273    color: #006666; 
     1274} 
     1275 
     1276.centrediv { 
     1277    width:50%; 
     1278    margin: 0 auto; 
     1279} 
  • main/trunk/greenstone3/web/interfaces/default/transform/config_format.xsl

    r31179 r31537  
    110110         when the XSLT processes it (which would then result in it 
    111111         being changed into a self-closing element, which then is 
    112          incorrectly rendered as HTML).  Doing thing with the 
     112         incorrectly rendered as HTML).  Doing this with the 
    113113         value-of is better then injecting an xsl:comment in 
    114114         (another approach we have used in the past) as the 
     
    116116         HTML.  This can lead to further complications if 
    117117         Javascript using the 'empty' div truely expects it to 
    118          have no connent of any form.  
     118         have no content of any form.  
    119119    --> 
    120120 
  • main/trunk/greenstone3/web/interfaces/default/transform/layouts/header.xsl

    r31399 r31537  
    409409     
    410410  </xsl:template> 
     411 
    411412  <xsl:template name="generateLoginURL"> 
    412413    <xsl:value-of select="$library_name"/> 
     
    431432    </xsl:for-each> 
    432433     
     434  </xsl:template> 
     435 
     436  <!-- Writing the reverse of generateLoginURL since the toggleUserMenuScript does a lot more than I want. --> 
     437  <!-- https://www.w3schools.com/xml/xsl_functions.asp#string -->   
     438  <xsl:template name="generateLogoutURL"> 
     439     
     440    <xsl:variable name="url" select="/page/pageRequest/@fullURL"/> 
     441    <xsl:variable name="tmpURL" select="substring-before($url, '&amp;amp;logout=')"/> 
     442    <xsl:variable name="beforeHash" select="substring-before($url, '#')"/> 
     443    <xsl:variable name="afterHash" select="substring-after($url, '#')"/> 
     444    <!-- Get rid of any lingering &amp;logout= already in the URL. 
     445     Can't use fn:replace() as it's only been defined since XSLT 2.0. We use XSLT 1.x --> 
     446    <xsl:variable name="fullURL"> 
     447      <xsl:choose> 
     448    <xsl:when test="$tmpURL != ''"><xsl:value-of select="$tmpURL" /></xsl:when> 
     449    <xsl:otherwise><xsl:value-of select="$url" /></xsl:otherwise> 
     450      </xsl:choose> 
     451    </xsl:variable> 
     452 
     453    <!-- Output the logout link: the current page's URL (with any lingering logout suffix removed)  
     454     followed by ?logout= or &amp;logout= followed by any # portion of the current page's URL --> 
     455    <xsl:choose> 
     456      <xsl:when test="$beforeHash != ''"><xsl:value-of select="$beforeHash" /></xsl:when> 
     457      <xsl:otherwise><xsl:value-of select="$fullURL" /></xsl:otherwise> 
     458    </xsl:choose> 
     459    <xsl:choose> 
     460      <xsl:when test="contains($fullURL, '?')"><xsl:text>&amp;logout=</xsl:text></xsl:when> 
     461      <xsl:otherwise>?logout=</xsl:otherwise> 
     462    </xsl:choose> 
     463    <xsl:if test="$afterHash != ''">#<xsl:value-of select="$afterHash" /></xsl:if>    
    433464  </xsl:template> 
    434465   
  • main/trunk/greenstone3/web/interfaces/default/transform/pages/document.xsl

    r31534 r31537  
    1212    <xsl:import href="layouts/main.xsl"/> 
    1313    <xsl:import href="layouts/toc.xsl"/> 
     14 
     15    <!-- templates for adding user comments --> 
     16    <xsl:import href="layouts/usercomments.xsl"/> 
    1417     
    1518    <xsl:variable name="bookswitch"> 
     
    367370                <xsl:call-template name="documentHeading"/><br/> 
    368371                <xsl:call-template name="documentContent"/> 
     372                <br /><xsl:call-template name="userCommentsSection"/> 
    369373            </xsl:when>  
    370374            <xsl:otherwise> <!-- display the standard greenstone document -->