// Copyright 1999 (c) by Perforce Software, Inc. All rights reserved. // // p4wFileBrowserPane: // The file browser pane displays the fstat information for several panes // which display information about a file (such as filelog, diffs, etc). #include <p4wp4.h> #include <assert.h> #include "p4wStrBuf.h" #include "p4wHtml.h" #include "p4wI18n.h" #include "p4wMenuPane.h" #include "p4wPane.h" #include "p4wFileBrowserPane.h" p4wFileBrowserPane::p4wFileBrowserPane( p4wView & ParentView, p4wRequest & Request, StrBuf & clientRoot ) : p4wPane(ParentView, Request), fIsWriteable(1), fIsEditable(0), fBeenHere(0), fSeenBegin(0), fInDepot(0), fBoxStarted(0), fHasHeadType(0), fShowHide(0) { fFirstErrorOnly = 0; fClientRoot.Set(clientRoot); } p4wFileBrowserPane::~p4wFileBrowserPane() { } void p4wFileBrowserPane::Begin() { } void p4wFileBrowserPane::Render( StrDict * varList ) { // // Process fstat output and print out the file's state if (fRequest.GetHeaderProcessed()) { // if this fRequest has already processed the initial fstat block, // then all we need to do is save off the thumbnails, discard the rest StrPtr * headRev = varList->GetVar( "headRev" ); StrPtr * attr_thumb = varList->GetVar( "attr-thumb" ); if (attr_thumb && headRev) fRequest.AddThumbNail(atoi(headRev->Text()), attr_thumb); return; } int hideDetails = fRequest.GetBrowseMode() ? 1 : 0; StrPtr * headType = varList->GetVar( "headType" ); StrPtr * action = varList->GetVar( "action" ); StrPtr * headRev = varList->GetVar( "headRev" ); StrPtr * haveRev = varList->GetVar( "haveRev" ); StrPtr * otherOpen = varList->GetVar( "otherOpen" ); StrPtr * headAction = varList->GetVar( "headAction" ); StrPtr * headChange = varList->GetVar( "headChange" ); StrPtr * otherLock = varList->GetVar( "otherLock" ); StrPtr * ourLock = varList->GetVar( "ourLock" ); StrPtr * unresolved = varList->GetVar( "unresolved" ); StrPtr * depotFile = varList->GetVar( "depotFile" ); StrPtr * cf = varList->GetVar( "clientFile" ); StrPtr * attrDigest_thumb = varList->GetVar( "attrDigest-thumb" ); StrPtr * change = varList->GetVar( "change" ); StrPtr * type = varList->GetVar( "type" ); StrPtr * fileSize = varList->GetVar( "fileSize" ); StrPtr * clientFile = NULL; StrPtr * workspaceFile = NULL; fRequest.SetHeaderProcessed(1); fRequest.SetHeadRev(headRev ? atoi(headRev->Text()) : 0); fRequest.SetHaveRev(haveRev ? atoi(haveRev->Text()) : 0); fRequest.SetHasThumbs(attrDigest_thumb ? 1 : 0); p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; // if filetype includes +1, set the correct lock flag if (headType) { fHasHeadType = 1; char * offset = strchr(headType->Text(), '+'); if (offset) { if (strchr(offset, 'l')) { if (action) ourLock = (StrPtr *)1; else if (otherOpen) otherLock = (StrPtr *)1; } } } // // 2002.1 servers and later yield all 3 mappings. // If "path" is set, we have them all, otherwise // workspaceFile is in clientFile. if( varList->GetVar( "path" ) ) { workspaceFile = varList->GetVar( "path" ); clientFile = varList->GetVar( "clientFile" ); } else { workspaceFile = varList->GetVar( "clientFile" ); } if (workspaceFile) fWorkspaceFile.Set(workspaceFile); else fWorkspaceFile.Set(""); // // Determine if file is in depot. This may be useful to // the View class so that it may decide not to send any // more requests. if( depotFile && depotFile->Length() ) fInDepot = 1; else fInDepot = 0; const StrPtr *srevState = fRequest.GetStateArg( "sr" ); const StrPtr *srev = fRequest.GetDynArg( "sr" ); const StrPtr *srev2 = fRequest.GetDynArg( "sr2" ); const StrPtr *rev1 = fRequest.GetDynArg( "rev1" ); // If both dynamic and state variables for sr are set, dynamic // is used. Set srev to the value to be used. Dynamic sr is // used for ala mode browsing. if( !srev && srevState ) srev = srevState; else if ( srev && srev2 && ((fRequest.GetCmd() == AC_CHGLISTANNOTATE) || (fRequest.GetCmd() == AC_CHGLISTFULLANNOTATE)) ) srev = srev2; // // The highest rev used for the file on this page may // be useful to the public (ie annotated text page). if (headRev) fHeadRev.Set( headRev->Text() ); else if( rev1 ) fHeadRev.Set( rev1->Text() ); // // The highest chg# used for the file on this page may // be useful to the public (ie annotated text page). if( headChange ) fHeadChange.Set( headChange->Text() ); // // Determine which paths we output and whether we're done // showing them. int depotDone = 1; int clientDone = 0; int workDone = 0; int workHasLink = 0; if( fRequest.GetViewMode() == VM_WORKSPACE && depotFile ) depotDone = 0; if( !clientFile ) clientDone = 1; if( fRequest.GetBrowseMode() || fRequest.GetBITBRev().Length() || srev || !workspaceFile ) workDone = 1; // // Additional state information int isDeleted = 0; if( headAction && ( *headAction == "delete" || *headAction == "move/delete" ) ) { ++isDeleted; } int isAdded = 0; if( action ) { if( *action == "add" || *action == "branch" || *action == "import" ) ++isAdded; if( *action == "edit" || *action == "add" ) fIsEditable++; } if( headType && ( strstr( headType->Text(), "text" ) || strstr( headType->Text(), "symlink" ) || strstr( headType->Text(), "unicode" ) || strstr( headType->Text(), "utf16" ) ) ) { fIsWriteable = 1; } else { if( isAdded ) { fIsWriteable = 1; } else { fIsWriteable = 0; } } // // Used for generating whitespace urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); // // Start the page. startPage( varList ); // // Output the linkified path (but only once on a page!) if ( fRequest.GetCmd() == AC_DIFF22 ) (void)doFileDirLink( 0, 0, 1 ); else (void)doFileDirLink( srev, rev1 ? rev1 : (fRequest.GetCmd() == AC_FILETEXTDEPOT ? headRev : 0), 1 ); if (!(fRequest.GetScreenChunks() & SCRN_FILTER)) return; // // Start the table: a box encloses the file state values startTables(1); htm.beginTRow(); htm.beginCol(); // // Display the file status, filtering out client info if we are in // browse-only mode. // // Generate type of file, if available if( headType ) { htm.beginSpan( "label" ); htm.text( "Type:" ); htm.endSpan(); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); htm.beginCol(); htm.text( headType->Text() ); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "30", "", 1 ); htm.endCol(); htm.beginCol(); } // // In workspace mode, show the depot path if available. // In other modes, the client path would be shown instead, // if available. Or.. the workspace path (if available). htm.beginSpan( "label" ); if( !depotDone ) { htm << "Depot:"; } else if( !clientDone ) { htm << "Client:"; } else if( !workDone ) { htm << "Workspace:"; } htm.endSpan(); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); htm.beginCol(); if( !depotDone ) { htm << depotFile; ++depotDone; } else if( !clientDone ) { htm << clientFile; ++clientDone; } else if( !workDone && (fRequest.isLocalRequest() || !SEC_DONT_SHOW_WKSP_LINKS) ) { fRequest << htm; htm.Clear(); workHasLink = RenderFileLink( workspaceFile ); ++workDone; } htm.endCol(); if (fileSize) { htm.beginCol(); htm.icon( clearIcon.Text(), "1", "30", "", 1 ); htm.endCol(); htm.beginCol(); htm.beginSpan( "label" ); htm << "Head rev size:"; htm.endSpan(); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); htm.beginCol(); char buf[16]; float f; unsigned u = (unsigned)atoi(fileSize->Text()); if (u < 1000) { htm << fileSize->Text() << " bytes"; } else if (u < 1000000) { f = u/1000.0; sprintf(buf, "%.2f KB", f); htm << buf; } else if (u < 1000000000) { f = u/1000000.0; sprintf(buf, "%.2f MB", f); htm << buf; } else { f = u/1000000000.0; sprintf(buf, "%.2f GB", f); htm << buf; } htm.endCol(); } htm.endTRow(); // // Show the have version only if available and appropriate. // It's not appropriate in browse-only mode. StrBuf mimeBase; StrBuf mimeURL; const StrPtr *cl = fRequest.GetStateArg( "cl" ); htm.beginTRow(); if( !fRequest.GetBrowseMode() && ( haveRev || ( !haveRev && !isAdded ) ) ) { htm.beginCol(); htm.beginSpan( "label" ); htm << "Have:"; htm.endSpan(); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); htm.beginCol(); if( !fRequest.GetBrowseMode() ) { if( haveRev == NULL || !depotFile ) { if( !isAdded ) htm << "#none"; } else { mimeBase << "//" << fRequest.GetHTTPPort() << "/@rev1=have"; if (cl) mimeBase << "&cl=" << cl->Text(); mimeBase << "@" << depotFile->Text(); urlMaker.ConstructURL( mimeURL, mimeBase.Text(), AC_MIMECONTENT, NULL, fRequest.GetUnicode() ); htm.beginLink( mimeURL.Text(), NULL, NULL, NULL, "Open current revision in browser" ); htm << "#" << haveRev; htm.endLink(); } } htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "30", "", 1 ); htm.endCol(); } // // Show another path if we have any left to show (client // or workspace) if( !clientDone || (!workDone && (fRequest.isLocalRequest() || !SEC_DONT_SHOW_WKSP_LINKS)) ) { htm.beginCol(); htm.beginSpan( "label" ); if( !clientDone ) { htm << "Client:"; } else if( !workDone ) { htm << "Workspace:"; } htm.endSpan(); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); htm.beginCol(); if( !clientDone ) { htm << clientFile; ++clientDone; } else if( !workDone ) { fRequest << htm; htm.Clear(); workHasLink = RenderFileLink( workspaceFile ); ++workDone; } htm.endCol(); } htm.endTRow(); if( headRev || !workDone ) htm.beginTRow(); // // Show the head revision if available if( headRev && depotFile ) { htm.beginCol(); htm.beginSpan( "label" ); htm << "Head:"; htm.endSpan(); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); htm.beginCol(); mimeBase.Clear(); mimeBase << "//" << fRequest.GetHTTPPort() << "/@rev1=head@" << depotFile->Text(); urlMaker.ConstructURL( mimeURL, mimeBase.Text(), AC_MIMECONTENT, NULL, fRequest.GetUnicode() ); htm.beginLink( mimeURL.Text(), NULL, NULL, NULL, "Open head revision in browser" ); htm << "#" << headRev; htm.endLink(); if ( srev ) { StrBuf tempBuf; tempBuf.Append(" (as of @"); tempBuf.Append(srev); tempBuf.Append(")"); htm << tempBuf; } htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "30", "", 1 ); htm.endCol(); } // // Show the last path (workspace) if available if( !workDone && (fRequest.isLocalRequest() || !SEC_DONT_SHOW_WKSP_LINKS) ) { htm.beginCol(); htm.beginSpan( "label" ); htm << "Workspace:"; htm.endSpan(); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); htm.beginCol(); fRequest << htm; htm.Clear(); workHasLink = RenderFileLink( workspaceFile ); htm.endCol(); } else if (workDone && workHasLink && !fRequest.isLocalRequest() && fRequest.GetViewMode() != VM_WORKSPACE) { htm.beginCol(0,0,"2"); htm.endCol(); htm.beginCol("top",0,0,0,0,0,0,1); htm.beginSpan("remote"); htm << "(workspace files are located on remote machine \"" << fRequest.GetHost() << "\")"; htm.endSpan(); htm.endCol(); } if( headRev || !workDone ) htm.endTRow(); // // Generate the informational icons htm.beginTRow(); htm.beginCol( NULL, NULL, "11" ); htm.beginTable(0,0,"0","0"); htm.beginTRow(); htm.beginCol(0,0,0,0,0,0,0,1); fRequest << htm; htm.Clear(); if( isDeleted ) { OutputIcon( "/headdeletedIcon", 17, 13, "Deleted at Head Revison", 0 ); fRequest << " deleted at head revision "; } if( !fRequest.GetBrowseMode() && action ) { int b = 1; if ( *action == "edit") { OutputIcon( "/editIcon", 17, 8, "Opened for Edit", 0 ); fRequest << " opened for edit "; } else if( *action == "delete" ) { OutputIcon( "/deleteIcon", 17, 8, "Opened for Delete", 0 ); fRequest << " opened for delete "; } else if( *action == "add" ) { OutputIcon( "/addIcon", 17, 8, "Opened for Add", 0 ); fRequest << " opened for add "; } else if( *action == "branch" || *action == "import" ) { OutputIcon( "/branchIcon", 17, 8, "Opened for Branch", 0 ); fRequest << " opened for branch "; } else if( *action == "integrate" ) { OutputIcon( "/integIcon", 17, 8, "Opened for Integrate", 0 ); fRequest << " opened for integrate "; } else b = 0; if (b && type && headType && strcmp(type->Text(), headType->Text())) fRequest << " <nobr><span class=\"filestat\"><font color=\"#D08080\">(as " << type->Text() << ") </font></span></nobr>"; if (!hideDetails) { p4wHtml htm; StrBuf changeURL; StrPtr * owner = varList->GetVar( "actionOwner" ); int b = NotOpenedByOwner( owner ); int d = !strcmp(change->Text(), "default"); if( b && d ) { StrBuf newBase; StrBufDict cmdArgs; newBase << owner << "@" << fRequest.GetClient(); cmdArgs.SetVar( "arg", newBase.Text() ); fRequest.UseNewBase( newBase, NULL, "path", NULL ); urlMaker.ConstructURL( changeURL, newBase.Text(), AC_DESCRIBEDEAFULT, &cmdArgs, fRequest.GetUnicode() ); } else { StrBuf newBase; StrBufDict cmdArgs; cmdArgs.SetVar( "arg", change ); fRequest.UseNewBase( newBase, NULL, "path", NULL ); urlMaker.ConstructURL( changeURL, newBase.Text(), AC_CHANGEPENDINGEDIT, d ? NULL : &cmdArgs, fRequest.GetUnicode() ); } htm.beginLink( changeURL.Text() ); if (d) htm << "Default"; else htm << p4wStrBuf().EscapeHTML( *change, Unicode() ).Text(); htm.endLink(); fRequest << htm << " "; } else fRequest << " "; } if( !fRequest.GetBrowseMode() && ourLock ) { OutputIcon( "/lockedIcon", 17, 7, "Locked by You", 0 ); fRequest << " locked "; } // // Flag if this file is out of sync only if this is // not browse-only mode. if( !fRequest.GetBrowseMode() ) { if( !isDeleted ) { // // File is not mapped on this client if( ( haveRev && !headRev ) || ( !isAdded && !haveRev && !headRev && action ) || ( !cf && !haveRev ) ) { OutputIcon( "/unmappedIcon", 17, 13, "Not in Client View", 0 ); fRequest << " file not mapped in client view "; } else if( !haveRev ) { if( !isAdded ) { OutputIcon( "/syncednoneIcon", 17, 13, "Not in workspace", 0 ); fRequest << " not in workspace "; } } else if( headRev && haveRev && ( strcmp( headRev->Text(), haveRev->Text() ) != 0 ) ) { p4wHtml htm(1); StrBuf url; StrBuf baseWPath; OutputIcon( "/syncIcon", 17, 13, "Needs Sync", 0 ); fRequest << " needs "; fRequest.UseNewBase( baseWPath, NULL, "path", fRequest.GetPath().Text() ); urlMaker.ConstructURL( url, baseWPath.Text(), AC_SYNCFILE, NULL, fRequest.GetUnicode() ); htm.beginLink( url.Text(), 0,0,0, "Sync to head revision" ); htm << "sync"; htm.endLink(); fRequest << htm << " "; } } if( unresolved ) { p4wHtml htm(1); StrBuf url; StrBuf baseWPath; OutputIcon( "/resolveIcon", 17, 13, "Needs Resolve", 0 ); fRequest << " needs "; fRequest.UseNewBase( baseWPath, NULL, "path", fRequest.GetPath().Text() ); urlMaker.ConstructURL( url, baseWPath.Text(), AC_RESOLVEFILEFRM, NULL, fRequest.GetUnicode() ); url << "&mu=" << GetFileMenuShowVals(varList); htm.beginLink( url.Text(), 0,0,0, "Resolve file..." ); htm << "resolve"; htm.endLink(); fRequest << htm << " "; } } if( otherLock ) { OutputIcon( "/theirlockIcon", 17, 7, "Locked by Other(s)", 0 ); // // Locked by user is only available from 2002.1 StrPtr *ot = varList->GetVar( StrRef( "otherLock" ), 0 ); if( ot ) fRequest << " locked by " << ot; else fRequest << " locked by other user"; fRequest << " "; } // // If we are in browse-only mode, our action is considered // opened by others. if( otherOpen || ( fRequest.GetBrowseMode() && action ) ) { p4wHtml htm(1); StrPtr *p; OutputIcon( "/theiropenIcon", 17, 8, "Opened by Other(s)", 0 ); htm << " opened by "; if( otherOpen ) { for( int i = 0; (p = varList->GetVar( StrRef( "otherOpen" ), i ) ) != NULL; i++ ) { if( i > 0 ) { htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); } htm.endCol(); htm.beginCol("bottom",0,0,0,0,"100%",0,1); htm << p4wStrBuf().EscapeHTML( *p, Unicode() ); if( !hideDetails && !fRequest.GetBrowseMode() ) { StrPtr *oo = p; p = varList->GetVar( StrRef( "otherAction" ), i ); if (p) htm << " " << p4wStrBuf().EscapeHTML( *p, Unicode() ); p = varList->GetVar( StrRef( "otherChange" ), i ); if (p) { StrBuf changeURL; StrBuf newBase; StrBufDict cmdArgs; if (*(p->Text()) == 'd') *(p->Text()) = 'D'; fRequest.UseNewBase( newBase, NULL, "path", NULL ); if (isdigit(*(p->Text()))) { cmdArgs.SetVar( "arg", p ); urlMaker.ConstructURL( changeURL, newBase.Text(), AC_CHANGEPENDINGEDIT, &cmdArgs, fRequest.GetUnicode() ); } else { StrBuf newBase; StrBufDict cmdArgs; cmdArgs.SetVar( "arg", oo->Text() ); fRequest.UseNewBase( newBase, NULL, "path", NULL ); urlMaker.ConstructURL( changeURL, newBase.Text(), AC_DESCRIBEDEAFULT, &cmdArgs, fRequest.GetUnicode() ); } htm << " "; htm.beginLink( changeURL.Text() ); htm << p4wStrBuf().EscapeHTML( *p, Unicode() ); htm.endLink(); } } } } if( fRequest.GetBrowseMode() && action ) { if( otherOpen ) { htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); htm.endCol(); htm.beginCol("bottom",0,0,0,0,"100%",0,1); } htm << fRequest.GetUser() << "@"; htm << fRequest.GetClient() << crlf; } else htm << crlf; fRequest << htm; } htm.endCol(); htm.endTRow(); htm.endTable(); fRequest << htm; // // If back in time, display a link to return to present if( fRequest.GetBITBRev().Length() > 0 ) { StrBuf returnURL; fRequest.UseNewBase( returnURL, NULL, "sr", NULL ); fRequest.UseNewBase( returnURL, returnURL.Text(), "path", fRequest.GetPath().Text()); OutputHREF( "Back-in-Time Browsing® is <i>on</i> -- click ", returnURL.Text(), AC_BROWSEFILE, NULL, "here", " to turn it off." ); } htm.endCol(); htm.endTRow(); fRequest << htm; } int p4wFileBrowserPane::NotOpenedByOwner( const StrPtr *owner ) { // // Return 1 if we can prove the current file is // not opened by the current user. This condition // may only be detected in 2002.1 and later servers // which have the actionOwner field in fstat. StrPtr *noCase = fRequest.GetProtocol( "nocase" ); if( !owner ) return 0; // // Use appropriate comparison based on the // case-sensitivity of the server. if( noCase && owner->CCompare( fRequest.GetUser() ) || !noCase && owner->XCompare( fRequest.GetUser() ) ) return 1; return 0; } void p4wFileBrowserPane::End() { if( fBoxStarted ) { // // End the pane. p4wHtml htm; endTables(); fBoxStarted = 0; htm.comment( "END FILEBROWSER PANE" ); fRequest << htm; } } void p4wFileBrowserPane::RenderError( char *err, int escapeHTML ) { p4wHtml htm; if( !fBeenHere ) { startPage(); (void)doFileDirLink( NULL, NULL ); startTables(); } p4wPane::RenderErrorTbl( err, escapeHTML ); } void p4wFileBrowserPane::HandleError(Error *err) { // // If we are in workspace mode, and we get "no such file" error, // generate the file state info p4wHtml htm; if( err->GetSeverity() == E_WARN && err->GetGeneric() == EV_EMPTY && fRequest.GetViewMode() == VM_WORKSPACE ) { StrBuf errMsg; err->Fmt( &errMsg ); char *e = strstr( errMsg.Text(), "- no such file" ); char *c = strstr( errMsg.Text(), "- file(s) not in client" ); if( e || c ) { doWSFileState(); return; } } // // Not in workspace mode, but the file doesn't exist is now // considered fatal if( err->GetSeverity() == E_WARN && err->GetGeneric() == EV_EMPTY && fRequest.GetViewMode() != VM_WORKSPACE ) { StrBuf err2; err->Fmt( &err2 ); char *e2 = strstr( err2.Text(), "- no such file" ); if( e2 ) { startPage(); (void)doFileDirLink( NULL, NULL ); PromptRetry( "The path entered could not be found." ); fHadFatalError = 1; return; } } // // Handle case where path doesn't exist because the // the first node of the path is incorrect. if( err->GetSeverity() == E_FAILED && err->GetGeneric() == EV_UNKNOWN && fRequest.GetViewMode() != VM_WORKSPACE ) { StrBuf err3; err->Fmt( &err3 ); if ( strstr( err3.Text(), "- must refer to client" ) ) { startPage(); (void)doFileDirLink( NULL, NULL ); PromptRetry( "The path entered could not be found." ); fHadFatalError = 1; return; } } // // Handle case where file is not under client root (workspace mode) if( err->GetSeverity() == E_FAILED && err->GetGeneric() == EV_CONTEXT && fRequest.GetViewMode() == VM_WORKSPACE ) { StrBuf err4; err->Fmt( &err4 ); if( strstr( err4.Text(), "not under client's" ) ) { (void)doWSFileState(); return; } } // // ...or handle the error DoHandleError( err, 0 ); } int p4wFileBrowserPane::RenderFileLink( StrPtr *workspaceFile ) { // // Generate link using AC_WORKSPACEFILE since // file:/// no longer works (used for workspace file link). // Don't generate link if file is empty or doesn't exist FileSys *f = FileSys::Create( FST_TEXT ); f->Set( *workspaceFile ); FileSysType ftype = f->CheckType(); int statFlags = f->Stat(); delete f; if( ftype == FST_MISSING || ftype == FST_EMPTY ) return 0; if( !fRequest.isLocalRequest() && (statFlags & FSF_SYMLINK) && !SEC_ALLOW_CR8CHG_SYMLINKS ) return 0; p4wHtml htm(1); StrBuf url; url.Set( fRequest.IsHTTPS() ? "https://" : "http://" ); url << fRequest.GetHTTPPort() << "/"; // // Chop off the root, make \ into / if required # ifdef OS_NT StrBuf temp; temp.Set(workspaceFile); char *p = temp.Text(); char *q = p; while (q = strchr(q, '\\')) *q = '/'; int len = fClientRoot.Length(); if (*(p+len) != '/') { char *r = fClientRoot.Text() + len; while (*--r == '\\' && len--) *r = '\0'; fClientRoot.SetLength(); len = fClientRoot.Length(); } # else char *p = workspaceFile->Text(); int len = fClientRoot.Length(); if (*(p+len) != '/') { char *r = fClientRoot.Text() + len; while (*--r == '/' && len--) *r = '\0'; fClientRoot.SetLength(); len = fClientRoot.Length(); } # endif if (strcmp(fClientRoot.Text(), "null")) assert(*(p+len) == '/'); StrBuf relativeFile; relativeFile.Set(p+1+len); StrBuf link; if( fRequest.GetUnicode() ) { // // Browsers which are not MSIE need to have the // URI converted to the local character set, and // then have the characters escaped. IE, however, // must have the URI in UTF-8. if( fRequest.GetJavascriptMode() != 2 ) { char *cvtPath = NULL; CharSetCvt *cvt = CharSetCvt::FindCvt( CharSetCvt::UTF_8, CharSetCvt::Lookup( fRequest.GetClientApi().GetCharset().Text() ) ); cvtPath = p4wI18n::convertText( cvt, relativeFile.Text() ); if( !cvtPath ) { link << p4wStrBuf().EscapeURL( relativeFile, 0 ); } else { link << p4wStrBuf().EscapeURL( StrRef( cvtPath ), 0 ); delete [] cvtPath; } delete cvt; } else { // MSIE link << p4wStrBuf().EscapeURL( relativeFile, 1 ); } } else { if( fRequest.GetJavascriptMode() == 2 ) link << relativeFile; else link << p4wStrBuf().EscapeURLAllChars( relativeFile, 0 ); } p = link.Text(); while (p = strstr(p, "%2F")) { *p++ = '/'; strcpy(p, p+2); } link.SetLength(); url << link << "?ac=" << AC_WORKSPACEFILE; htm.beginLink( url.Text(), NULL, NULL, NULL, "Open workspace file in browser" ); htm.text( p4wStrBuf().EscapeHTML( *workspaceFile, Unicode() ).Text() ); htm.endLink(); htm.linebreak(); fRequest << htm; return 1; } int p4wFileBrowserPane::doFileDirLink( const StrPtr *sr, const StrPtr *rev, int showhide ) { // Weird mapping can cause multiple fstat outputs. Insure that // only one directory header is rendered and that the truncated // data values are displayed correctly. p4wHtml htm; int status = 1; if( !fBeenHere ) { int useDepot = ( fRequest.GetViewMode() != VM_WORKSPACE ); htm.beginTRow(); htm.beginCol("top"); if (showhide && (fRequest.GetScreenChunks() & SCRN_FILTER)) { htm << crlf; htm << "<script language=javascript>" << crlf; htm << "function toggleFileDetails()" << crlf; htm << "{" << crlf; htm << "var elem, vis, viss, vish, vis1, vis2, vis3, vis4;" << crlf; htm << "if( document.getElementById )" << crlf; // this is the way the standards work htm << " elem = document.getElementById( 'showhideBlock' );" << crlf; htm << "else if( document.all )" << crlf; // this is the way old msie versions work htm << " elem = document.all['showhideBlock'];" << crlf; htm << "else if( document.layers )" << crlf; // this is the way nn4 works htm << " elem = document.layers['showhideBlock'];" << crlf; htm << "vis = elem.style;" << crlf; // if the style.display value is blank we try to figure it out here htm << "if(vis.display==''&&elem.offsetWidth!=undefined&&elem.offsetHeight!=undefined)" << crlf; htm << " vis.display = (elem.offsetWidth!=0&&elem.offsetHeight!=0)?'block':'none';" << crlf; htm << "vis.display = (vis.display==''||vis.display=='block')?'none':'block';" << crlf; htm << "if( document.getElementById )" << crlf; htm << " elem = document.getElementById( 'showBlockIcon' );" << crlf; htm << "else if( document.all )" << crlf; htm << " elem = document.all['showBlockIcon'];" << crlf; htm << "else if( document.layers )" << crlf; htm << " elem = document.layers['showBlockIcon'];" << crlf; htm << "viss = elem.style;" << crlf; htm << "viss.display = (vis.display==''||vis.display=='block')?'none':'inline';" << crlf; htm << "if( document.getElementById )" << crlf; htm << " elem = document.getElementById( 'hideBlockIcon' );" << crlf; htm << "else if( document.all )" << crlf; htm << " elem = document.all['hideBlockIcon'];" << crlf; htm << "else if( document.layers )" << crlf; htm << " elem = document.layers['hideBlockIcon'];" << crlf; htm << "vish = elem.style;" << crlf; htm << "vish.display = (vis.display==''||vis.display=='block')?'inline':'none';" << crlf; htm << "if( document.getElementById )" << crlf; htm << " elem = document.getElementById( 'showhideInline1' );" << crlf; htm << "else if( document.all )" << crlf; htm << " elem = document.all['showhideInline1'];" << crlf; htm << "else if( document.layers )" << crlf; htm << " elem = document.layers['showhideInline1'];" << crlf; htm << "vis1 = elem.style;" << crlf; htm << "vis1.display = (vis.display==''||vis.display=='block')?'inline':'none';" << crlf; htm << "if( document.getElementById )" << crlf; htm << " elem = document.getElementById( 'showhideInline2' );" << crlf; htm << "else if( document.all )" << crlf; htm << " elem = document.all['showhideInline2'];" << crlf; htm << "else if( document.layers )" << crlf; htm << " elem = document.layers['showhideInline2'];" << crlf; htm << "vis2 = elem.style;" << crlf; htm << "vis2.display = (vis.display==''||vis.display=='block')?'inline':'none';" << crlf; htm << "if( document.getElementById )" << crlf; htm << " elem = document.getElementById( 'showhideInline3' );" << crlf; htm << "else if( document.all )" << crlf; htm << " elem = document.all['showhideInline3'];" << crlf; htm << "else if( document.layers )" << crlf; htm << " elem = document.layers['showhideInline3'];" << crlf; htm << "vis3 = elem.style;" << crlf; htm << "vis3.display = (vis.display==''||vis.display=='block')?'inline':'none';" << crlf; htm << "if( document.getElementById )" << crlf; htm << " elem = document.getElementById( 'showhideInline4' );" << crlf; htm << "else if( document.all )" << crlf; htm << " elem = document.all['showhideInline4'];" << crlf; htm << "else if( document.layers )" << crlf; htm << " elem = document.layers['showhideInline4'];" << crlf; htm << "vis4 = elem.style;" << crlf; htm << "vis4.display = (vis.display==''||vis.display=='block')?'none':'inline';" << crlf; htm << "}" << crlf; htm << "document.write(\"<a href='javascript:toggleFileDetails();' title='Show/Hide File Details'>\")" << crlf; htm << "document.write(\"<div id='showBlockIcon'>\")" << crlf; htm << "document.write(\"<img src='/plusBoxIcon?ac=20' height='20' width='20' border='0' alt='Show File Details' title='Show File Details'>\")" << crlf; htm << "document.write(\"</div>\")" << crlf; htm << "document.write(\"<div id='hideBlockIcon'>\")" << crlf; htm << "document.write(\"<img src='/minusBoxIcon?ac=20' height='20' width='20' border='0' alt='Hide File Details' title='Hide File Details'>\")" << crlf; htm << "document.write(\"</div>\")" << crlf; htm << "document.write(\"</a>\")" << crlf; htm << "document.write(\"</td><td width='100%'>\")" << crlf; htm << "</script>" << crlf; } htm.beginSpan( "path" ); if (!showhide) htm << " "; fRequest << htm; htm.Clear(); status = OutputDirectoryHeader( fRequest.GetPath().Text(), 1, 1, useDepot ); switch( fRequest.GetCmd() ) { case AC_ANNOTATE: case AC_FULLANNOTATE: case AC_CHGLISTANNOTATE: case AC_CHGLISTFULLANNOTATE: { const StrPtr *rev1 = fRequest.GetDynArg( "rev1", HE_Page ); const StrPtr *rev2 = fRequest.GetDynArg( "rev2", HE_Page ); const StrPtr *sr = fRequest.GetDynArg( "sr", HE_Page ); const StrPtr *sr2 = fRequest.GetDynArg( "sr2", HE_Page ); if( rev1 ) htm << "#" << p4wStrBuf().EscapeHTML(*rev1, Unicode()); else if( sr ) htm << "@" << p4wStrBuf().EscapeHTML(*sr, Unicode()); if( rev2 ) { if( rev1 ) htm << ","; else htm << ",#"; htm << p4wStrBuf().EscapeHTML(*rev2, Unicode()); } else if( sr2 ) { if( sr ) htm << ","; else htm << ",@"; htm << p4wStrBuf().EscapeHTML(*sr2, Unicode()); } break; } default: if( rev && fRequest.GetCmd() != AC_DIFF2 ) htm << "#" << p4wStrBuf().EscapeHTML(*rev, Unicode()); else if( sr ) htm << "@" << p4wStrBuf().EscapeHTML(*sr, Unicode()); break; } htm.endSpan(); if (!fRequest.isLocalRequest() && fRequest.GetViewMode() == VM_WORKSPACE) { htm << " "; htm.beginSpan("remote"); htm << "<nobr>"; htm << "(workspace files are located on remote machine \"" << p4wStrBuf().EscapeHTML(fRequest.GetHost(), Unicode()) << "\")"; htm << "</nobr>"; htm.endSpan(); } htm.endCol(); htm.endTRow(); fBeenHere = 1; fRequest << htm; } return status; } void p4wFileBrowserPane::startTables(int showhide) { // // Start the table that generates the box around the file // status values p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; StrBuf grayIcon; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); urlMaker.ConstructURL( grayIcon, "/grayPixelIcon", AC_ICON, NULL ); htm.beginTRow(); htm.beginCol(0, 0, "2"); if ((fShowHide = showhide) != 0) { htm << "<script language=javascript>" << crlf; htm << "document.write(\"<div id='showhideBlock'>\")" << crlf; htm << "</script>" << crlf; } htm.beginTable( "0", "100%", "4", "0", "#FFFFFF" ); htm.beginTRow( NULL, NULL, "#FFFFFF" ); htm.beginCol(); htm.beginTable( "0", NULL, "2", "0", "#FFFFFF" ); fBoxStarted = 1; fRequest << htm; } void p4wFileBrowserPane::endTables() { // // Finish the tables that generate the box around the file // status values p4wHtml htm; p4wURL urlMaker; StrBuf grayIcon; StrBuf clearIcon; // // Used for generating whitespace urlMaker.ConstructURL( grayIcon, "/grayPixelIcon", AC_ICON, NULL ); urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); htm.endTable(); if (fShowHide) { htm << "<script language=javascript>" << crlf; htm << "document.write(\"</div>\")" << crlf; htm << "</script>" << crlf; } htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol( "top", 0, "3", 0, 0, "100%" ); htm << "<div id=\"showhideInline1\">" << crlf; htm.icon( grayIcon.Text(), "1", "100%", "", 1, "0", "0" ); htm << "</div>" << crlf; htm.endCol(); htm.endTRow(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.beginCol(NULL, NULL, NULL, NULL, NULL, "10"); htm.icon( clearIcon.Text(), "1", "10", "", 1 ); htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); fRequest << htm; } void p4wFileBrowserPane::doWSFileState() { // // Generate the file state information for a workspace file // that is not under perforce control p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; // // Used for generating whitespace urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); // // Start the page if we haven't already int show = FM_BROWSEFILE+FM_ADDFILEFRM+FM_FILETEXTLOCAL+FM_WORKSPACEFILEPATH; startPage( 0, show ); // // Output the linkified path (but only once on a page!). // If there is something wrong with the filename, don't render // the rest of the page. if( !doFileDirLink( NULL, NULL ) ) { if( !fWRootFound ) PromptRetry( "The path entered is not under workspace root." ); else PromptRetry( "The path entered could not be found." ); return; } if (!fRequest.isLocalRequest() && SEC_DONT_SHOW_WKSP_LINKS) return; // // Start the table: a box encloses the file state values startTables(); htm.beginTRow(); htm.beginCol(); htm.beginSpan( "label" ); htm.text( "Workspace:" ); htm.endSpan(); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "1", "5", "", 1 ); htm.endCol(); htm.beginCol(); fRequest << htm; htm.Clear(); // // If this server supports special characters in filenames, // unescape any escaped characters StrBuf fname; const StrPtr *server = fRequest.GetProtocol( "server2" ); if( server && server->Atoi() > 17 ) { fname.Set( p4wStrBuf().UnescapeP4Chars( fRequest.GetPath() ) ); } else { fname.Set( fRequest.GetPath() ); } RenderFileLink( (StrPtr *)&fname ); htm.endCol(); htm.endTRow(); fRequest << htm; } void p4wFileBrowserPane::startPage(StrDict * varList, int mushow) { // // Begin the pane by starting the table and printing // the page title. The page title is determined by ac. p4wHtml htm(1); p4wURL urlMaker; StrBuf title; StrBuf grayIcon; StrBuf clearIcon; AllCommands ac; const StrPtr *rev1 = fRequest.GetDynArg( "rev1", HE_Page ); const StrPtr *rev2 = fRequest.GetDynArg( "rev2", HE_Page ); StrBuf baseWMPath; if( fSeenBegin ) return; ++fSeenBegin; // // Start the page. If we are in workspace mode and this // file is not in the depot, we will generate a File State // page instead of a Revision History page, if Revision History // was chosen. if ( fRequest.GetCmd() == AC_LAUNCHEDITOR && !fRequest.isLocalRequest() ) { ac = AC_EDITTEXTLOCAL; } else if( ( fRequest.GetCmd() == AC_BROWSEFILE && fRequest.GetViewMode() == VM_WORKSPACE && !fInDepot ) || fRequest.GetCmd() == AC_LAUNCHEDITOR ) { ac = AC_FILESTATE; } else { ac = fRequest.GetCmd(); if (ac == AC_EDITTEXTLOCALPROCESSOR) ac = AC_EDITTEXTLOCAL; if (ac == AC_UPLOADTOLOCALPROCESSOR) ac = AC_UPLOADTOLOCAL; } htm.comment( "BEGIN FILEBROWSER PANE" ); // // Construct the page title from the menu item title.Set( p4wMenuPane::GetMenuTitle( ac ) ); if( ac == AC_DIFF2 ) { // // If rev1 or rev 2 starts with a minus, the format is // -[revnbr][path] // Example: // &rev1=-4//depot/dir/filename.ext // this means run // p4 diff2 //depot/dir/filename.ext#4 fRequest.GetPath()#rev2 if( rev1 ) { if (*(rev1->Text()) == '-') { char *p = rev1->Text(); while (*++p && isdigit(*p)) ; title << p ; p = rev1->Text(); if (isdigit(*++p)) title << "#"; while (*p && isdigit(*p)) title.Append(p++, 1); } else title << "Revision #" << rev1; } else title << "(unknown)"; title << " vs. "; if( rev2 ) { if (*(rev2->Text()) == '-') { char *p = rev2->Text(); while (*++p && isdigit(*p)) ; title << p; p = rev2->Text(); if (isdigit(*++p)) title << "#"; while (*p && isdigit(*p)) title.Append(p++, 1); } else title << "Revision #" << rev2; } else title << "(unknown)"; } else if( ac == AC_DIFF22 ) { title << "Two Depot Files"; } switch(fRequest.GetCmd()) { case AC_SYNCFILE: case AC_SYNCPROCESSOR: case AC_REMOVEFILEFRM: case AC_EDITFILE: case AC_EDITPROCESSOR: case AC_ADDFILE: case AC_ADDPROCESSOR: case AC_DELETEFILE: case AC_DELETEPROCESSOR: case AC_FTYPEPROCESSOR: case AC_LOCKFILEFRM: case AC_UNLOCKFILEFRM: case AC_REVERTUNCHANGEDFILEFRM: case AC_REVERTCMD: case AC_REVERTFILE: case AC_INTEGPROCESSOR: case AC_RESOLVEPROCESSOR: if( fView.IsPreviewCmd() ) title << " Preview"; else title << " Status"; break; default: break; } title << ":"; // // Used to generate a line underneath the title urlMaker.ConstructURL( grayIcon, "/grayPixelIcon", AC_ICON, NULL ); urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); // // Workspace mode needs to use a full path in its urls fRequest.UseNewBase( baseWMPath, NULL, "path", fRequest.GetPath().Text() ); if (fRequest.GetScreenChunks() & SCRN_TITLE) { // Output the title htm.beginTRow(); htm.beginCol(0, 0, "3"); htm.beginTable(); // put the whole line in a table to prevent wrapping htm.beginTRow(); htm.beginCol(); htm.beginSpan( "title" ); htm << title << " "; htm.endSpan(); htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); // // Generate line underneath the title htm.beginTRow(); htm.beginCol( "top", 0, "3" ); htm.icon( grayIcon.Text(), "1", "100%", "", 1, "0", "0" ); htm.endCol(); htm.endTRow(); } if (!(fRequest.GetScreenChunks() & SCRN_FILTER)) { fRequest << htm; return; } // // Generate another table to hold the vertical border lines htm.beginTRow(); htm.beginCol(0, 0, 0, 0, 0, "100%"); htm.beginTable(0, "100%", "0", "0"); htm.beginTRow(); htm.beginCol(); htm.icon( clearIcon.Text(), "5", "5", "", 1 ); int show; if (varList) show = GetFileMenuShowVals(varList); else if (mushow) show = mushow; else { const StrPtr *mu = fRequest.GetDynArg("mu"); show = mu ? atoi(mu->Text()) : -1; } htm.SetRequest(&fRequest); htm.RenderFileMenu(show ? show : -1); htm << "<script language=javascript>" << crlf; htm << "setmushow("; htm << show; htm << ");" << crlf; htm << "</script>" << crlf; htm.endCol(); htm.endTRow(); fRequest << htm; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 12234 | Matt Attaway |
Rejigger P4Web project in preparation for official sunsetting The bin directory contains the last official builds of P4Web from the Perforce download site. P4Web is soon to be completely sunsetted; these builds are here for folks who don't want to build their own. To better handle the archived builds the source code has been moved into a separate src directory. |
||
//guest/perforce_software/p4web/Panes/p4wFileBrowserPane.cpp | |||||
#1 | 8914 | Matt Attaway | Initial add of the P4Web source code |