Containerized deployment
docker run -i -t -d -p 80:80 --name onlyoffice onlyoffice/documentserver
Verify the service by accessing the server address in a browser.
Locking specific cells
- Open the Excel workbook, select all cells (
Ctrl+A) and open the Format Cells dialog (Ctrl+1). On the Protection tab, uncheck theLockedoption. - Individually select the cells that should remain protected and mark them as locked via the same dialog.
- Go to
Review > Protect Sheet. In the permissions list, keepSelect unlocked cellschecked, and clear the check forSelect locked cells. Apply the protection.
Back‑end project setup
Download the .NET example from the OnlyOffice website (Communiyt Edition) and unpack it. Inside settings.config, adjust the storage location and your own server’s base URL.
Enabling manual save callback
Locate DocEditor.aspx.cs and set the customization flag so that the editor triggers a callback when the user clicks the Save button:
editorConfig.customization.forcesave = true;
Calback endpoint
The editor posts status updates to the URL defined by CallBackUrl in DocEditor.aspx. The request is handled by WebEditor.ashx.cs. A manual save produces a MustForceSave state, which invokes the Track method. From the incoming JSON we extract the download URL of the latest file version and pass it to our processing logic.
Extracting cell values The callback handler downloads the file content and uses EPPlus to read specific cells:
private void ProcessSavedFile(string downloadUrl, HttpContext context)
{
var fileBytes = new WebClient().DownloadData(downloadUrl);
using (var stream = new MemoryStream(fileBytes))
using (var package = new ExcelPackage(stream))
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
var ws = package.Workbook.Worksheets[0];
int trackLength = Convert.ToInt32(ws.Cells[2, 6].Value);
int stealCount = Convert.ToInt32(ws.Cells[5, 6].Value);
decimal minSpan = Math.Round(Convert.ToDecimal(ws.Cells[6, 6].Value), 1);
decimal platLen = Math.Round(Convert.ToDecimal(ws.Cells[7, 6].Value), 1);
string remarks = ws.Cells[4, 7].Value?.ToString() ?? "";
SaveToDatabase(trackLength, stealCount, minSpan, platLen);
SaveWordReport(remarks, context);
}
}
Persisting to Oracle Parameterised queries replace the previous string concatenation to avoid SQL injection and simplify maintenance.
private void SaveToDatabase(int trackLength, int stealCount, decimal minSpan, decimal platLen)
{
using var conn = new OracleConnection("Data Source=127.0.0.1:1521/orcl;User Id=adu;Password=123456;");
conn.Open();
using var lookup = conn.CreateCommand();
lookup.CommandText = "SELECT Id FROM TableData WHERE TrackLength = :track";
lookup.Parameters.Add(new OracleParameter("track", trackLength));
var existingId = lookup.ExecuteScalar() as string;
if (!string.IsNullOrEmpty(existingId))
{
using var upd = conn.CreateCommand();
upd.CommandText = "UPDATE TableData SET StealCount=:steal, MinSpan=:span, PlatLength=:plat WHERE Id=:id";
upd.Parameters.Add(new OracleParameter("steal", stealCount));
upd.Parameters.Add(new OracleParameter("span", minSpan));
upd.Parameters.Add(new OracleParameter("plat", platLen));
upd.Parameters.Add(new OracleParameter("id", existingId));
upd.ExecuteNonQuery();
}
else
{
using var ins = conn.CreateCommand();
ins.CommandText = "INSERT INTO TableData(Id, TrackLength, StealCount, MinSpan, PlatLength) VALUES(sys_guid(),:track,:steal,:span,:plat)";
ins.Parameters.Add(new OracleParameter("track", trackLength));
ins.Parameters.Add(new OracleParameter("steal", stealCount));
ins.Parameters.Add(new OracleParameter("span", minSpan));
ins.Parameters.Add(new OracleParameter("plat", platLen));
ins.ExecuteNonQuery();
}
}
Generating a Word report The extracted notes are placed into a new Word document and stored on the server.
private void SaveWordReport(string content, HttpContext context)
{
string fileName = Guid.NewGuid().ToString("N") + ".docx";
string fullPath = Path.Combine(context.Server.MapPath("~/App_Data"), fileName);
var document = new XWPFDocument();
var paragraph = document.CreateParagraph();
var run = paragraph.CreateRun();
run.SetText(content);
using var fs = new FileStream(fullPath, FileMode.CreateNew);
document.Write(fs);
// Additional metadata or logging can be performed here.
}