Skip to content

Commit

Permalink
Merge pull request #15 from freeflowuniverse/development_live_collabo…
Browse files Browse the repository at this point in the history
…ration

Add live collaboration
  • Loading branch information
rawdaGastan authored Oct 9, 2023
2 parents ac5201e + cfc88bd commit 404fcee
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 25 deletions.
41 changes: 29 additions & 12 deletions pkg/httpd/api_onlyoffice.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@ import (
"path"
"time"

"github.com/drakkan/sftpgo/v2/pkg/dataprovider"
"github.com/go-chi/render"
)

var supportedOnlyOfficeExtensions = []string{
"doc", "docx", "odt", "ppt", "pptx", "xls", "xlsx", "ods",
}

// only office environment variables
const (
// ServerAdressEnvKey Key for ServerAddress env variable
ServerAdressEnvKey = "SFTP_SERVER_ADDR"
// OnlyOfficeServerAdressEnvKey Key for OnlyOfficeServerAddress env variable
OnlyOfficeServerAdressEnvKey = "ONLYOFFICE_SERVER_ADDR"
// ServerAddressEnvKey Key for ServerAddress env variable
ServerAddressEnvKey = "SFTP_SERVER_ADDR"
// OnlyOfficeServerAddressEnvKey Key for OnlyOfficeServerAddress env variable
OnlyOfficeServerAddressEnvKey = "ONLYOFFICE_SERVER_ADDR"
)

type onlyofficeCallbackData struct {
type onlyOfficeCallbackData struct {
Status int `json:"status"`
URL string `json:"url"`
}
Expand All @@ -43,18 +45,20 @@ type editOnlyOfficeFilePage struct {
Ext string
Token string
User userInfo
ShareID string
DocumentURL string
}

type onlyOfficeCallbackResponse struct {
Error int `json:"error"`
}

func getServerAddress() string {
return os.Getenv(ServerAdressEnvKey)
return os.Getenv(ServerAddressEnvKey)
}

func getOnlyOfficeServerAddress() string {
return os.Getenv(OnlyOfficeServerAdressEnvKey)
return os.Getenv(OnlyOfficeServerAddressEnvKey)
}

func generateOnlyOfficeFileKey(fileName string, modTime time.Time) string {
Expand All @@ -76,14 +80,27 @@ func checkOnlyOfficeExt(fileName string) bool {
return false
}

func onlyOfficeWriteCallback(w http.ResponseWriter, r *http.Request) {
connection, err := getUserConnection(w, r)
if err != nil {
return
func (s *httpdServer) onlyOfficeWriteCallback(w http.ResponseWriter, r *http.Request) {
var connection *Connection
var err error

shareID := r.URL.Query().Get("id")
if shareID != "" {
validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
_, connection, err = s.checkPublicShare(w, r, validScopes)
if err != nil {
return
}
} else {
connection, err = getUserConnection(w, r)
if err != nil {
return
}
}

fileName := connection.User.GetCleanedPath(r.URL.Query().Get("path"))

callbackData := onlyofficeCallbackData{}
callbackData := onlyOfficeCallbackData{}

err = render.DecodeJSON(r.Body, &callbackData)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion pkg/httpd/api_shares.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,11 @@ func (s *httpdServer) checkPublicShare(w http.ResponseWriter, r *http.Request, v
}
}

shareID := getURLParam(r, "id")
shareID := r.URL.Query().Get("id")
if shareID == "" {
shareID = getURLParam(r, "id")
}

share, err := dataprovider.ShareExists(shareID, "")
if err != nil {
statusCode := getRespStatus(err)
Expand Down
1 change: 0 additions & 1 deletion pkg/httpd/httpd.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ const (
webClientGetPDFPathDefault = "/web/client/getpdf"
webStaticFilesPathDefault = "/static"
webOpenAPIPathDefault = "/openapi"

// MaxRestoreSize defines the max size for the loaddata input file
MaxRestoreSize = 10485760 // 10 MB
maxRequestSize = 1048576 // 1MB
Expand Down
2 changes: 1 addition & 1 deletion pkg/httpd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,7 @@ func (s *httpdServer) initializeRouter() {
Post(userUploadFilePath, uploadUserFile)
router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientWriteDisabled)).
Patch(userFilesDirsMetadataPath, setFileDirMetadata)
router.With(s.checkAuthRequirements).Post(onlyOfficeCallbackPath, onlyOfficeWriteCallback)
router.With(s.checkAuthRequirements).Post(onlyOfficeCallbackPath, s.onlyOfficeWriteCallback)
})

if s.renderOpenAPI {
Expand Down
51 changes: 46 additions & 5 deletions pkg/httpd/webclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ type clientSharesPage struct {
baseClientPage
Shares []dataprovider.Share
BasePublicSharesURL string
EditPublicSharesURL string
}

type clientSharePage struct {
Expand Down Expand Up @@ -706,11 +707,28 @@ func (s *httpdServer) renderClientMFAPage(w http.ResponseWriter, r *http.Request
}

func (s *httpdServer) renderEditFilePage(w http.ResponseWriter, r *http.Request, fileName, fileData string, readOnly bool) {
var connection *Connection
var err error
if !readOnly && checkOnlyOfficeExt(fileName) {
connection, err := getUserConnection(w, r)
if err != nil {
s.renderInternalServerErrorPage(w, r, err)
return
// id is share ID
shareID := r.URL.Query().Get("id")
documentURL := getServerAddress()
if shareID != "" {
validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}
_, connection, err = s.checkPublicShare(w, r, validScopes)
if err != nil {
return
}
documentURL += "/web/client/pubshares/" + shareID
} else {
connection, err = getUserConnection(w, r)
if err != nil {
s.renderInternalServerErrorPage(w, r, err)
return
}
path := url.QueryEscape(fileName)
tokenString := jwtauth.TokenFromCookie(r)
documentURL += fmt.Sprintf("/api/v2/user/files?path=%s&jwt=%s", path, tokenString)
}
name := connection.User.GetCleanedPath(fileName)
info, err := connection.Stat(name, 0)
Expand All @@ -731,6 +749,8 @@ func (s *httpdServer) renderEditFilePage(w http.ResponseWriter, r *http.Request,
Name: connection.User.Username,
ID: strconv.Itoa(int(connection.User.ID)),
},
ShareID: shareID,
DocumentURL: documentURL,
}
renderClientTemplate(w, templateClientEditOfficeFile, data)
return
Expand Down Expand Up @@ -1001,6 +1021,10 @@ func (s *httpdServer) handleShareGetDirContents(w http.ResponseWriter, r *http.R
res["url"] = getFileObjectURL(share.GetRelativePath(name), info.Name(),
path.Join(webClientPubSharesPath, share.ShareID, "browse"))
res["last_modified"] = getFileObjectModTime(info.ModTime())
if info.Size() < httpdMaxEditFileSize {
res["edit_url"] = getFileObjectURL(name, info.Name(),
webClientEditFilePath) + fmt.Sprintf("&id=%s", share.ShareID)
}
results = append(results, res)
}

Expand Down Expand Up @@ -1210,8 +1234,24 @@ func (s *httpdServer) handleClientEditFile(w http.ResponseWriter, r *http.Reques
s.renderClientForbiddenPage(w, r, "Invalid token claims")
return
}
username := claims.Username

user, err := dataprovider.GetUserWithGroupSettings(claims.Username, "")
shareID := r.URL.Query().Get("id")
if shareID != "" {
validScopes := []dataprovider.ShareScope{dataprovider.ShareScopeRead, dataprovider.ShareScopeReadWrite}

q := r.URL.Query()
q.Add("id", shareID)
r.URL.RawQuery = q.Encode()

share, _, err := s.checkPublicShare(w, r, validScopes)
if err != nil {
return
}
username = share.Username
}

user, err := dataprovider.GetUserWithGroupSettings(username, "")
if err != nil {
s.renderClientMessagePage(w, r, "Unable to retrieve your user", "", getRespStatus(err), nil, "")
return
Expand Down Expand Up @@ -1442,6 +1482,7 @@ func (s *httpdServer) handleClientGetShares(w http.ResponseWriter, r *http.Reque
baseClientPage: s.getBaseClientPageData(pageClientSharesTitle, webClientSharesPath, r),
Shares: shares,
BasePublicSharesURL: webClientPubSharesPath,
EditPublicSharesURL: webClientEditFilePathDefault,
}
renderClientTemplate(w, templateClientShares, data)
}
Expand Down
4 changes: 2 additions & 2 deletions templates/webclient/editfile-office.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
"fileType": "{{.Ext}}",
"key": "{{.FileKey}}",
"title": "{{.FileName}}",
"url": "{{.BaseURL}}/api/v2/user/files?path={{.FilePath}}&jwt={{.Token}}"
"url": "{{.DocumentURL}}"
},
"editorConfig": {
"callbackUrl": "{{.BaseURL}}/api/v2/user/onlyoffice?path={{.FilePath}}&jwt={{.Token}}",
"callbackUrl": "{{.BaseURL}}/api/v2/user/onlyoffice?path={{.FilePath}}&jwt={{.Token}}&id={{.ShareID}}",
"user": {
"id": "{{.User.ID}}",
"name": "{{.User.Name}}"
Expand Down
33 changes: 32 additions & 1 deletion templates/webclient/sharefiles.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ <h6 class="m-0 font-weight-bold"><a href="{{.FilesURL}}?path=%2F"><i class="fas
<th>Name</th>
<th>Size</th>
<th>Last modified</th>
<th></th>
</tr>
</thead>
</table>
Expand Down Expand Up @@ -115,6 +116,18 @@ <h5 class="modal-title" id="uploadFilesModalLabel">
<script src="{{.StaticURL}}/vendor/filepond/filepond.min.js"></script>
<script type="text/javascript">
var spinnerDone = false;
var onlyOfficeExt = [
"doc", "docx", "odt", "ppt", "pptx", "xls", "xlsx", "ods",
]

function checkOnlyOfficeExt(extension) {
for (i = 0; i < onlyOfficeExt.length; i++) {
if (extension == onlyOfficeExt[i]) {
return true
}
}
return false
}

function shortenData(d, cutoff) {
if ( typeof d !== 'string' ) {
Expand Down Expand Up @@ -506,7 +519,25 @@ <h5 class="modal-title" id="uploadFilesModalLabel">
return data;
}
},
{ "data": "last_modified" }
{ "data": "last_modified" },
{ "data": "edit_url",
"render": function (data, type, row) {
if (type === 'display') {
let filename = escapeHTML(row["name"]);
let extension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
if (data){
if (checkOnlyOfficeExt(extension)){
{{if eq .Scope 1 }}
return "";
{{else}}
return `<a href="${data}"><i class="fas fa-edit"></i></a>`;
{{end}}
}
}
}
return "";
}
}
],
"buttons": [],
"lengthChange": true,
Expand Down
11 changes: 9 additions & 2 deletions templates/webclient/shares.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ <h6 class="m-0 font-weight-bold text-primary">View and manage shares</h6>
<th>Scope</th>
<th>Info</th>
<th></th>
<th>Path</th>
</tr>
</thead>
<tbody>
Expand All @@ -57,6 +58,7 @@ <h6 class="m-0 font-weight-bold text-primary">View and manage shares</h6>
<td>{{.GetScopeAsString}}</td>
<td>{{.GetInfoString}}</td>
<td>{{if .IsExpired}}1{{else}}0{{end}}</td>
<td>{{index .Paths 0}}</td>
</tr>
{{end}}
</tbody>
Expand Down Expand Up @@ -111,7 +113,8 @@ <h5 class="modal-title" id="linkModalLabel">
<p>If the share consists of a single file you can download it uncompressed using this <a id="readUncompressedLink" href="#" target="_blank">link</a>.</p>
</div>
<div id="writeShare">
<p>You can upload one or more files to the shared directory using this <a id="writePageLink" href="#" target="_blank">page</a></p>
<p>You can upload one or more files to the shared directory using this <a id="writePageLink" href="#" target="_blank">page</a></p>
<p>You can edit shared file using this <a id="editPageLink" href="#" target="_blank">page</a></p>
</div>
<div id="expiredShare">
This share is no longer accessible because it has expired
Expand Down Expand Up @@ -214,12 +217,14 @@ <h5 class="modal-title" id="linkModalLabel">
var shareID = shareData[0];
var shareScope = shareData[2];
var isExpired = shareData[4];
var sharePath = shareData[5];
if (isExpired == "1"){
$('#expiredShare').show();
$('#writeShare').hide();
$('#readShare').hide();
} else {
var shareURL = '{{.BasePublicSharesURL}}' + "/" + fixedEncodeURIComponent(shareID);
var editURL = '{{.EditPublicSharesURL}}' + "?path=" + sharePath + "&id=" + fixedEncodeURIComponent(shareID);
if (shareScope == 'Read'){
$('#expiredShare').hide();
$('#writeShare').hide();
Expand All @@ -236,6 +241,8 @@ <h5 class="modal-title" id="linkModalLabel">
$('#readShare').hide();
$('#writePageLink').attr("href", shareURL+"/upload");
$('#writePageLink').attr("title", shareURL+"/upload");
$('#editPageLink').attr("href", editURL);
$('#editPageLink').attr("title", editURL);
}
}
$('#linkModal').modal('show');
Expand All @@ -253,7 +260,7 @@ <h5 class="modal-title" id="linkModalLabel">
"buttons": [],
"columnDefs": [
{
"targets": [0, 4],
"targets": [0, 4, 5],
"visible": false,
"searchable": false
}
Expand Down

0 comments on commit 404fcee

Please sign in to comment.