// // Copyright 2003 Perforce Software. All rights reserved. // // This file is part of Perforce - the FAST SCM System. // // p4wFilelogPane: // Displays filelog for specified file on the file browser page #include <p4wp4.h> #include "p4wStrBuf.h" #include "p4wHtml.h" #include "p4wPane.h" #include "p4wFilelogPane.h" #include "base64.h" p4wFilelogPane::p4wFilelogPane( p4wView & ParentView, p4wRequest & Request ) : p4wPane( ParentView, Request ), fSeenData(0), fRowCtr(0), fExtended(0) { } p4wFilelogPane::~p4wFilelogPane() { } void p4wFilelogPane::Begin() { // // Begin the pane. p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; // // Construct whitespace spacer urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); htm.comment( "BEGIN FILELOG PANE" ); // // Start the form and table htm.beginTable( "0", "100%", "0", "1" ); htm.beginTRow(); htm.beginCol(NULL, NULL, NULL, NULL, NULL, "8"); htm.icon( clearIcon.Text(), "1", "8", "", 1 ); htm.endCol(); htm.beginCol(); htm.beginTable( "0", "100%", "0", "1" ); htm.beginTRow(); htm.beginCol(); htm.icon( clearIcon.Text(), "10", "1", "", 1 ); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); p4wStrBuf curFi; curFi.EscapeURLAllChars(fRequest.GetURL(), fRequest.GetUnicode()); StrBuf actionURL; actionURL << curFi.Text() << "?ac=" << AC_DIFFSVR; htm.beginForm(actionURL.Text(), "filelogForm"); htm.hiddenField("rev2", 0); htm.beginTable( 0, 0, 0, "0", 0, "rev_history" ); htm.beginTRow(); // // Generate table headings const StrPtr *thx = fRequest.GetStateArg( "thx" ); if (!thx) { const StrPtr *rev1 = fRequest.GetDynArg("rev1"); if (!rev1 || *rev1 != "1") { const StrPtr *flags = fRequest.GetCmdFlag(); if (fRequest.GetHeadRev() > 1 || (flags && *(flags->Text()) == '-')) { htm.beginDiv("diff2Help", "ttip", "display:none"); htm << "<nobr>To diff 2 revisions:"; htm.linebreak(); htm << " 1) Select radio button for first revision"; htm.linebreak(); htm << " 2) Choose "Diff vs. Selected Revision" from the "; htm.linebreak(); htm << " drop down menu of the second revision.</nobr>"; htm.endDiv(); htm << "<script language=javascript>" << crlf; htm << "document.write(\"<th align='center' style='padding:0 0'><center>"; htm << "<div id=\\\"diff2Icon\\\">"; htm << "<a onMouseOver='showDiv(\\\"diff2Help\\\", \\\"diff2Icon\\\")'"; htm << " onMouseOut='hideDiv(\\\"diff2Help\\\")'>"; htm << "<img src='/rundiff17Icon?ac=20' height='17' width='21' border='0' alt='' "; htm << "title=''>"; htm << "</div></a></center></th>"; htm << "<th style='padding:0 0'><img src='/clearpixelIcon?ac=20' height='1' width='1' border='0' alt='' title=''></th>\")"; htm << "</script>" << crlf; } else { htm << "<script language=javascript>" << crlf; htm << "document.write(\"<th style='padding:0 0'></th><th style='padding:0 0'></th>\")"; htm << "</script>" << crlf; } } else { htm << "<script language=javascript>" << crlf; htm << "document.write(\"<th style='padding:0 0'></th><th style='padding:0 0'></th>\")"; htm << "</script>" << crlf; } } htm.beginColHead( NULL, "left" ); htm << "Rev"; htm.icon( clearIcon.Text(), "0", "78", "", 1 ); htm.endColHead(); htm.beginColHead( NULL, "left" ); htm << "Changelist"; htm.endColHead(); htm.beginColHead( NULL, "left" ); htm << "Date"; htm.endColHead(); htm.beginColHead( NULL, "left" ); htm << "User"; htm.endColHead(); htm.beginColHead( NULL, "left" ); htm << "Type"; htm.endColHead(); htm.beginColHead( NULL, "left", 0, 0, 0, "60%" ); htm << "Changelist Description/Action"; htm.endColHead(); htm.endTRow(); fRequest << htm; } void p4wFilelogPane::Render( StrDict * varList ) { // // Output this filelog entries for each revision, including // data from extended filelog, if applicable. StrPtr *val; StrPtr *changeNo; StrPtr *client; StrPtr *user; StrBuf baseNoPath; StrBuf baseWMPath; StrBuf describeBase; StrBuf changeBase; StrBuf mimeBase; StrBuf erevBase; StrBuf describePath; StrBuf diffPath; const StrPtr *depotPath = varList->GetVar( "depotFile" ); const StrPtr *erevPath; p4wURL urlMaker; p4wHtml htm(1); StrBuf diffIcon; StrBuf clearIcon; StrBuf textIcon; StrBuf altText; StrBuf descText; int lineDone = 0; int doText; const StrPtr *thx = fRequest.GetStateArg( "thx" ); // // Construct the spacing icon urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); // // Construct some commonly used base paths. Describe URLs need // to use a new base that does not include a relative path. // Workspace mode paths need to use a full path, not a relative path. fRequest.UseNewBase( baseNoPath, NULL, "path", NULL ); fRequest.UseNewBase( baseWMPath, NULL, "path", fRequest.GetPath().Text() ); // // Construct any icons we may use urlMaker.ConstructIcon( textIcon, "/showtextsmallIcon", 18, 25, "View revision text", 1 ); // // Render may be called more than once if this is an extended // filelog. The first time the depotPath matches the // url's file path. Subsequent calls will require sending a // new base since the path may be different from the BASE path. if( !fSeenData ) { fSeenData = 1; changeBase.Set( fRequest.GetURL() ); } else { fRequest.UseNewBase( changeBase, NULL, "path", depotPath->Text() ); // // Since this is not the first time this was called, // we know that this is from a branch. Start the row, // and generate the link to the branch's path. htm.beginTRow( 0, 0, 0, (fRowCtr & 1) ? "top_row alt_row" : "top_row"); lineDone++; htm.beginCol( NULL, NULL, "12" ); fRequest << htm; htm.Clear(); (void)OutputDirectoryHeader( depotPath->Text(), 1, 1, 1 ); htm.endCol(); htm.endTRow(); fRequest << htm; htm.Clear(); fExtended++; } // // Parse the filelog entry int i, j; for( i = 0; ( val = varList->GetVar( StrRef("rev"), i ) ) != NULL; i++) { // // If page content limit in bytes has been exceeded, don't // generate this entry if( PageLimitExceeded() ) return; // // Row starts with a line at the top. If this is the first // entry of a branch, this line was already rendered. // // Start the line of data if( !lineDone ) { lineDone++; htm.beginTRow( "top", 0, 0, (fRowCtr & 1) ? "alt_row top_row" : "top_row" ); } else htm.beginTRow( "top", 0, 0, (fRowCtr & 1) ? "alt_row" : 0); // // Determine if we will have a link to diff between // this revision and its predecessor. Also, find out // if file is writeable to see if we generate a link // to print it. StrPtr *action = varList->GetVar( StrRef("action"), i ); StrPtr *type = varList->GetVar( StrRef("type"), i ); int isWriteable; int doDiff = 0; int revNo = atoi( val->Text() ); if( type && ( strstr(type->Text(), "text") || strstr(type->Text(), "symlink") || strstr(type->Text(), "unicode") || strstr(type->Text(), "utf16") ) ) { isWriteable = 1; } else { isWriteable = 0; } doText = ( isWriteable && ( strcmp( action->Text(), "delete" ) ) ); if( ( revNo > 1 ) && ( ( !strcmp( action->Text(), "edit" ) ) || ( !strcmp( action->Text(), "integrate" ) ) ) ) { doDiff = 1; } // // If not deleted at a given revision, show file by mime type // at the changelist associated with that revision. Need to // send the full url so that the "sr" variable can be passed // in the state variable space. // p4wHtml revURL; StrBuf revPath; changeNo = varList->GetVar( StrRef("change"), i ); fRequest.UseNewBase( mimeBase, NULL, "sr", changeNo->Text() ); fRequest.UseNewBase( mimeBase, mimeBase.Text(), "path", depotPath->Text()); int bThumb = 0; if (( strcmp( action->Text(), "delete") ) ) { urlMaker.ConstructURL( revPath, mimeBase.Text(), AC_MIMECONTENT, NULL, fRequest.GetUnicode() ); int only1rev = 0; if (!thx) { if (i == 0 && revNo == 1 && (!strcmp( action->Text(), "add" ) || !fRequest.GetDynArg( "fl" ))) only1rev = 1; htm << "<script language=javascript>" << crlf; htm << "document.write(\"<td rowspan='2'"; if (i) htm << " class='top_line'"; if (!only1rev) { htm << ">\")" << crlf; htm << "document.write(\"<Input type=radio name='revs' value='"; if (fExtended) htm << "-"; htm << val; if (fExtended) htm << depotPath->Text(); htm << "' id='"; htm << val; if (fExtended) htm << depotPath->Text(); htm << "' title='Select #" << val << "'>"; htm << "</td><td rowspan='2' bgcolor='#dfe3e8' style='padding:0 0'></td>\")" << crlf; } else htm << " style='padding:0 0'> </td><td rowspan='2' style='padding:0 0'> </td>\")" << crlf; htm << "</script>" << crlf; } htm.beginCol(0, 0, 0, "1", 0, 0, 0, 0, i ? "top_line" : 0); htm.beginLink( revPath.Text(), NULL, NULL, NULL, "Open revision in browser" ); if (val->Atoi() == fRequest.GetHaveRev()) htm.beginDiv(0,0,fRequest.GetHaveRev() == fRequest.GetHeadRev() ? "display: inline; border: solid 2px #66cc00; background: #EEEEEE;" : "display: inline; border: solid 2px #e5c700; background: #EEEEEE;"); htm << "<b> " << val << " </b>"; if (val->Atoi() == fRequest.GetHaveRev()) htm.endDiv(); htm.endLink(); if (!fExtended && !only1rev && !thx) { htm << "<script language=javascript>" << crlf; htm << "document.write(\"<span class='muaro'><a "; htm << "title='Menu' href='javascript:showMenu(\\\""; p4wStrBuf curFi; curFi.EscapeURLAllChars(fRequest.GetURL(), fRequest.GetUnicode()); if (strchr(curFi.Text(), '%')) { p4wStrBuf curFi2; curFi2.EscapeURLAllChars(curFi, fRequest.GetUnicode()); htm << curFi2.Text(); } else htm << curFi.Text(); int show; if (doText && doDiff) show = -1; else if (doText) show = RH_MIMECONTENT|RH_FILETEXTDEPOT|RH_SEPARATOR|RH_DIFFRVW|RH_DIFFSVR|RH_SEPARATOR2|RH_SYNCREV; else if (doDiff) show = RH_MIMECONTENT|RH_SEPARATOR|RH_DIFF21|RH_DIFFRVW|RH_DIFFSVR|RH_SEPARATOR2|RH_SYNCREV; else show = RH_MIMECONTENT|RH_SEPARATOR|RH_DIFFRVW|RH_DIFFSVR|RH_SEPARATOR2|RH_SYNCREV; htm << "\\\",\\\""; char buf[32]; sprintf(buf, "%d_%s", fExtended, val->Text()); htm << buf; htm << "\\\",\\\"revhist\\\","; htm << show; htm << ",\\\"&rev1=" << val << "\\\")"; htm << "' onClick='"; htm << "document.forms.filelogForm.rev2.value=\\\""; htm << val; htm << "\\\";' id='id_" << buf << "'>"; htm << " "; htm << "</a></span>\")" << crlf; htm << "</script>" << crlf; } if (fRequest.GetHasThumbs()) // TRUE -> protocol > 19 && !fRequest.HideThumbnails() { p4wHtml prefix; p4wHtml suffix; prefix.linebreak(); fRequest << htm; htm.Clear(); ThumbNailItem *t = fRequest.GetThumbNail(atoi(val->Text())); const StrPtr *thz = fRequest.GetStateArg( "thz" ); const StrPtr *thm = fRequest.GetStateArg( "thm" ); const StrPtr *thb = fRequest.GetStateArg( "thb" ); if (!t || !t->rev) thm = thb = thx = NULL; char *pcolstr; int colwidth; if (thz && thm) { if (!strcmp(thz->Text(), "s")) { pcolstr = "80"; colwidth = 80; } else { pcolstr = "120"; colwidth = 120; } } else { pcolstr = "160"; colwidth = 160; } StrBuf image; int h=0; int w=0; const char *pBorder = thb ? "1" : "0"; if (thm) // if we are to magnify the small thumbnails { StrBuf bindata; char * src = t->thumbnail.Text(); char * dest = bindata.Alloc(48); int lgth = b64_decode(src, 48, dest, 48); // get the png header only if (lgth > 24) { h = *((unsigned char *)dest+23); w = *((unsigned char *)dest+19); } else thm = NULL; } if (thm && w >= h && thm && w < colwidth) { image << "<img border=\""; image << pBorder; image << "\" width=\""; image << pcolstr; image << "\" src=\""; } else if (thm && w < h && h < colwidth) { image << "<img border=\""; image << pBorder; image << "\" height=\""; image << pcolstr; image << "\" src=\""; } else { image << "<img border=\""; image << pBorder; image << "\" src=\""; } if( fRequest.GetJavascriptMode() == 2 || !t || !t->rev) // MSIE (or no thumb found) { int b = 0; if (!b && t && t->rev && strlen(t->thumbnail.Text()) < 2030) { char buf[2048*2]; sprintf(buf, "http%s//%s/%s?ac=%d", fRequest.IsHTTPS() ? "s:" : ":", fRequest.GetHTTPPort().Text(), t->thumbnail.Text(), AC_ECHOURLAsDATA); if (strlen(buf) <= 2040) // can we embed the thumbnail in a URL for MSIE? { image << buf; b = 1; } } if (!b) { int l; p4wURL urlMaker; StrBuf actionURL; // build a URL that will get the thumb from the cache (or rerun the fstat) StrBufDict cmdArgs2; cmdArgs2.SetVar( "sr", changeNo->Text() ); urlMaker.ConstructURL( actionURL, revPath.Text(), AC_GETTHUMBNAIL, NULL, fRequest.GetUnicode() ); image << actionURL.Text(); } } else { image << "data:image/png;base64,"; image << t->thumbnail.Text(); } image << "\" alt=\""; image << changeNo->Text(); image << "\" /></center>"; OutputHREF( prefix.Text(), revPath.Text(), AC_NONE, NULL, image.Text(), suffix.Text() ); bThumb = 1; } htm.endCol(); } else { htm << "<script language=javascript>" << crlf; htm << "document.write(\"<td rowspan='2'"; if (i) htm << " class='top_line'"; htm << "></td><td rowspan='2' bgcolor='#dfe3e8' style='padding:0 0'></td>\")" << crlf; htm << "</script>" << crlf; htm.beginCol(0, 0, 0, "1", 0, 0, 0, 0, i ? "top_line" : 0); htm << "<b>" << val << "</b>"; htm.endCol(); } // // Generate the link to the changelist. If the changelist // is "default", don't generate a link (since this would // be from a spec depot). changeNo = varList->GetVar( StrRef("change"), i ); describeBase.Set( baseNoPath ); describeBase << changeNo; urlMaker.ConstructURL( describePath, describeBase.Text(), AC_DESCRIBE, NULL, fRequest.GetUnicode() ); htm.beginCol(0, 0, 0, "2", 0, 0, 0, 0, i ? "top_line" : 0); client = varList->GetVar( StrRef("client"), i ); user = varList->GetVar( StrRef("user"), i ); if( *changeNo != "default" && *client != "remote" && *user != "remote" ) { htm.beginLink( describePath.Text(), NULL, NULL, NULL, "View changelist" ); htm << p4wStrBuf().EscapeHTML( *changeNo, Unicode() ).Text(); htm.endLink(); } else { htm << p4wStrBuf().EscapeHTML( *changeNo, Unicode() ).Text(); } htm.endCol(); // // Date htm.beginCol(0, 0, 0, "2", 0, 0, 0, 0, i ? "top_line" : 0); htm << p4wStrBuf().ExpandList( StrRef("%time@*Y/*m/*d%"), *varList, i, Unicode() ).Text(); htm.endCol(); // // User htm.beginCol(0, 0, 0, "2", 0, 0, 0, 0, i ? "top_line" : 0); descText.Clear(); descText << p4wStrBuf().ExpandList( StrRef("%user%"), *varList, i, Unicode() ).Text(); if (descText.Length()) htm << descText; else htm << " "; htm.endCol(); // // Type htm.beginCol(0, 0, 0, "2", 0, 0, 0, 0, i ? "top_line" : 0); htm << type->Text(); htm.endCol(); // // Description htm.beginCol(0, 0, 0, 0, 0, 0, 0, 0, i ? "top_line" : 0); descText.Clear(); descText << p4wStrBuf().ExpandList(StrRef("%desc%"), *varList, i, Unicode(), 1 ); htm.text( descText.Length() ? descText.Text() : " " ); htm.endCol(); htm.endTRow(); htm.beginTRow(0, 0, 0, (fRowCtr & 1) ? "alt_row" : 0); // // Generate icons/links to view depot text and/or diff2 if // appropriate htm.beginCol("bottom"); if ( doText || doDiff ) { for( j = 0; (val = varList->GetVar(StrRef("erev"), i, j)) != NULL; j++ ) ; while (j-- > 0) htm.linebreak(); } htm.beginNobreak(); if( doText || doDiff ) htm.icon( clearIcon.Text(), "1", "28", "", 1 ); if( doText ) { StrBufDict tempArgs; tempArgs.SetVar( "rev1", varList->GetVar( StrRef("rev"), i )->Text() ); if( fRequest.GetViewMode() == VM_WORKSPACE ) { urlMaker.ConstructURL( revPath, baseWMPath.Text(), AC_FILETEXTDEPOT, &tempArgs, fRequest.GetUnicode() ); } else { fRequest.ConstructSafeURL( revPath, changeBase.Text(), AC_FILETEXTDEPOT, &tempArgs ); } htm.beginLink( revPath.Text(), NULL, NULL, NULL, "View revision text" ); htm << textIcon; htm.endLink(); if (doDiff) htm << crlf; } if( doDiff ) { p4wStrBuf rev1; rev1 << ( revNo - 1 ); StrPtr * rev2 = varList->GetVar( StrRef("rev"), i ); StrBufDict args; args.SetVar( "rev1", rev1 ); args.SetVar( "rev2", rev2 ); if( fRequest.GetViewMode() == VM_WORKSPACE ) { urlMaker.ConstructURL( diffPath, baseWMPath.Text(), AC_DIFF2, &args, fRequest.GetUnicode() ); } else { fRequest.ConstructSafeURL( diffPath, changeBase.Text(), AC_DIFF2, &args ); } altText.Set( "Diff rev #" ); altText << rev1 << " vs. rev #" << rev2; urlMaker.ConstructIcon( diffIcon, "/rundiffprevsmallIcon", 18, 25, altText.Text(), 1 ); htm.beginLink( diffPath.Text() ); htm << diffIcon; htm.endLink(); } else if( !doText ) htm << " "; htm.endNobreak(); htm.endCol(); // // Action htm.beginCol(0, 0, 0, 0, 0, 0, 0, 1, "top_line"); htm << action; // Branch/merge info for( j = 0; (val = varList->GetVar(StrRef("erev"), i, j)) != NULL; j++ ) { erevPath = varList->GetVar(StrRef("file"), i, j); htm.linebreak(); htm << p4wStrBuf().ExpandArray( StrRef("%how% "), *varList, i, j, Unicode() ); fRequest << htm; htm.Clear(); if( fRequest.GetViewMode() != VM_WORKSPACE ) { fRequest.UseNewBase( erevBase, NULL, "path", erevPath->Text() ); } else { fRequest.UseNewBase( erevBase, NULL, "md", "d" ); fRequest.UseNewBase( erevBase, erevBase.Text(), "cd", "//" ); fRequest.UseNewBase( erevBase, erevBase.Text(), "wr", NULL ); fRequest.UseNewBase( erevBase, erevBase.Text(), "cdf", NULL ); fRequest.UseNewBase( erevBase, erevBase.Text(), "path", erevPath->Text() ); } OutputHREF( NULL, erevBase.Text(), AC_BROWSEFILE, NULL, erevPath->Text(), p4wStrBuf().EscapeHTML(*val, Unicode()).Text() ); fRequest << htm; htm.Clear(); } htm.endCol(); htm.endTRow(); fRequest << htm; htm.Clear(); fRowCtr++; } } void p4wFilelogPane::End() { // // End the filelog list p4wHtml htm; if( !fSeenData && !fPrintedError ) { doUnderline(); htm.beginTRow(); htm.beginCol( NULL, NULL, "4" ); htm << "Filelog not available."; htm.endCol(); htm.endTRow(); } // // End the pane. htm.endTable(); htm.endForm(); htm.endCol(); htm.endTRow(); htm.comment( "END FILELOG PANE" ); fRequest << htm; } void p4wFilelogPane::RenderError( char *data, int escapeHTML ) { doUnderline(); RenderErrorTbl( data, escapeHTML ); }
# | 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/p4wFilelogPane.cpp | |||||
#1 | 8914 | Matt Attaway | Initial add of the P4Web source code |