این مقاله در مشارکت با ایجاد شده است فقط دفتر. از شما برای حمایت از شرکایی که SitePoint را امکان پذیر می سازند متشکریم.

هر وقت متوجه شدیم که سعی می کنیم عملکرد پیچیده ای را به برنامه ای اضافه کنیم ، این س arال پیش می آید که “آیا باید خودم را رول کنم؟” و مگر اینکه هدف شما ایجاد چنین عملکردی باشد ، پاسخ تقریباً همیشه “نه” مستقیم است.

آنچه شما نیاز دارید چیزی است که به شما کمک می کند هرچه سریعتر به یک MVP برسید و بهترین راه برای دستیابی به آن استفاده از یک راه حل کامل خارج از جعبه است که می تواند به شما در صرفه جویی در وقت کمک کند ، که به نوبه خود ترجمه می شود صرفه جویی در هزینه های توسعه.

من فرض می کنم که شما هنوز اینجا هستید زیرا موارد فوق با شما طنین انداز است. بنابراین ، اکنون که ما در حال همگام سازی هستیم ، آنچه می خواهم در این مقاله به شما نشان دهم آسان بودن ادغام OnlyOffice در برنامه وب شماست.

OnlyOffice چیست؟

از جانب وب سایت آنها:

OnlyOffice دارای غنی ترین ویژگی مجموعه اداری موجود است که با فرمت های فایل های Microsoft Office و OpenDocument بسیار سازگار است. مستقیماً از برنامه وب خود ، اسناد ، صفحات گسترده و ارائه ها را مشاهده ، ویرایش و کار کنید.

مجموعه دفتر دارای چندین نسخه است. در این مقاله قصد داریم از آن استفاده کنیم نسخه توسعه دهنده، زیرا ما می خواهیم ویراستاران را در برنامه ادغام کنیم که بعداً به عنوان سرویس ابری یا نصب پیش فرض به بسیاری از کاربران تحویل داده می شود.

اگر می خواهید از OnlyOffice در یک راه حل موجود برای همگام سازی و اشتراک استفاده کنید ، باید نسخه Enterprise را بررسی کنید. لیستی از ادغام ها موجود است اینجا.

نسخه توسعه دهنده

نسخه Developer نه تنها به شما آزادی کافی را می دهد تا ویرایشگران را در برنامه خود ادغام کنید ، بلکه همچنین با گزینه “White Label” همراه است که به شما امکان می دهد ویراستاران را به طور کامل سفارشی کنید تا از آنها با نام تجاری خود استفاده کنید.

ادغام سرور سند

برای ادغام با برنامه وب خود ، ابتدا باید بارگیری کنید فقط اسناد آفیس (بسته بندی شده به عنوان سرور اسناد) و آن را بر روی سرور محلی خود تنظیم کنید.

پس از نصب آن می توانید درخواست های مربوط به رسیدگی به اسناد را در سرور خود شروع کنید. OnlyOffice موارد بسیار خوبی را ارائه می دهد مثال ها برای .خالص، جاوا، Node.js، PHP، پایتون و یاقوت.

می توانید Document Server و مثال دلخواه خود را بارگیری کرده و بلافاصله آن را بر روی دستگاه خود امتحان کنید.

من نشان خواهم داد که چگونه می توانید شروع به ادغام در برنامه خود کنید. برای این منظور ، ما از یک مثال بسیار ساده با Node.js و Express استفاده خواهیم کرد. من جزئیات بیشتری در مورد اجرا نخواهم داد ، موارد ضروری استخوان را بیرون می آورم و به شما اجازه می دهم جاهای خالی را برای ایجاد یک سیستم قوی و مقیاس پذیر پر کنید.

من یک برنامه با ساختار زیر دارم:

- node_modules
- public
    - backups
    - css
        - main.css
    - documents
        - sample.docx
    - javascript
        - main.js
    - samples
        - new.docx
        - new.xlsx
        - new.pptx
- app.js
- index.html
- package.json

ما از public/documents پوشه برای ذخیره اسناد. app.js فایل جایی است که کد برنامه Express ما در آن قرار دارد و index.html جایی است که ما اسناد خود را نشان خواهیم داد. من افتادم sample.docx برای آزمایش در پوشه اسناد بایگانی کنید.

درخت داخل آن پرونده می کند public/samples/ پرونده های خالی هستند که هنگام “ایجاد” فایل های جدید کپی خواهیم کرد.

backups پوشه ، همانطور که بعدا خواهید دید ، نه تنها به ما کمک می کند نسخه های قبلی را پشتیبان گیری کنیم بلکه به ما کمک می کند تا شناسه منحصر به فرد اسناد خود را پس از اصلاح ایجاد کنیم.

public/css/main.css و public/javascript/main.js پرونده ها توسط index.html. بعدا بررسی خواهیم کرد

بیایید نگاهی به app.js فایل:

const express = require('express');
const bodyParser = require("body-parser");
const path = require('path');
const fs = require('fs');
const syncRequest = require('sync-request');

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.use(express.static("public"));

app.get("https://www.sitepoint.com/", (req, res) => {
  res.sendFile(path.join(__dirname, "/index.html"));
});

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`App listening on http://localhost:${port}`));

کاری که ما انجام می دهیم این است که به پرونده ها سرویس دهیم localhost:3000/documents/filename.

من هم از خودم پیشی گرفتم و اضافه کردم syncRequest، fs، و bodyParser. اینها در حال حاضر مربوط نیستند اما بعداً از آنها استفاده خواهیم کرد.

اسناد را واکشی کنید

برای نشان دادن اسناد موجود ، باید لیستی از تمام نام پرونده ها را دریافت کرده و برای مشتری ارسال کنیم. ما ایجاد خواهیم کرد /documents مسیر برای این:

app.get("/documents", (req, res) => {
  const docsPath = path.join(__dirname, "public/documents");
  const docsPaths = fs.readdirSync(docsPath);

  const fileNames = [];

  docsPaths.forEach(filePath => {
    const fileName = path.basename(filePath);
    fileNames.push(fileName);
  });

  res.send(fileNames);
});

اسناد ایجاد کنید

در آغاز ما فقط یک نمونه سند خواهیم داشت ، اما این اصلا جالب نیست. بیایید یک را اضافه کنیم /create مسیر برای کمک به ما در اضافه کردن برخی از پرونده ها. ما به سادگی یک fileName و الگوی مربوطه را در آن کپی کنید public/documents پوشه با نام جدید خود:

app.post("/create", async (req, res) => {
  const ext = path.extname(req.query.fileName);
  const fileName = req.query.fileName;

  const samplePath = path.join(__dirname, "public/samples", "new" + ext);
  const newFilePath = path.join(__dirname, "public/documents", fileName);

  
  try {
    fs.copyFileSync(samplePath, newFilePath);
    res.sendStatus(200);
  } catch (e) {
    res.sendStatus(400);
  }
});

اسناد را حذف کنید

ما همچنین به روشی برای حذف اسناد نیاز داریم. بیایید یک /delete مسیر:

app.delete("/delete", (req, res) => {
  const fileName = req.query.fileName;
  const filePath = path.join(__dirname, "public/documents", fileName);

  try {
    fs.unlinkSync(filePath);
    res.sendStatus(200);
  } catch (e) {
    res.sendStatus(400);
  }
});

این یکی فوق العاده ساده است. ما پرونده را حذف و ارسال خواهیم کرد 200 کد وضعیت برای اطلاع دادن به کاربر همه چیز خوب است. در غیر این صورت ، آنها یک 400 کد وضعیت

اسناد را ذخیره کنید

تا کنون ، ما می توانیم اسناد خود را برای ویرایش باز کنیم ، اما راهی برای ذخیره تغییرات خود نداریم. بیایید اکنون این کار را انجام دهیم. ما اضافه خواهیم کرد /track مسیر برای ذخیره پرونده های ما:

app.post("/track", async (req, res) => {
  const fileName = req.query.fileName;

  const backupFile = filePath => {
    const time = new Date().getTime();
    const ext = path.extname(filePath);
    const backupFolder = path.join(__dirname, "public/backups", fileName + "-history");

    
    !fs.existsSync(backupFolder) && fs.mkdirSync(backupFolder);

    
    const previousBackup = fs.readdirSync(backupFolder)[0];
    previousBackup && fs.unlinkSync(path.join(backupFolder, previousBackup));

    const backupPath = path.join(backupFolder, time + ext);

    fs.copyFileSync(filePath, backupPath);
  }

  const updateFile = async (response, body, path) => {
    if (body.status == 2) {
      backupFile(path);
      const file = syncRequest("GET", body.url);
      fs.writeFileSync(path, file.getBody());
    }

    response.write("{"error":0}");
    response.end();
  }

  const readbody = (request, response, path) => {
    const content = "";
    request.on("data", function (data) {
      content += data;
    });
    request.on("end", function () {
      const body = JSON.parse(content);
      updateFile(response, body, path);
    });
  }

  if (req.body.hasOwnProperty("status")) {
    const filePath = path.join(__dirname, "public/documents", fileName);
    updateFile(res, req.body, filePath);
  } else {
    readbody(req, res, filePath);
  }
});

این یک مشکل است ، زیرا وقتی فایل توسط ویرایشگر ذخیره می شود ، توسط Document Server استفاده می شود. همانطور که می بینید ، ما در حال بازگشت هستیم "{"error":0}"، که به سرور می گوید همه چیز خوب است.

وقتی ویرایشگر بسته شد ، نسخه فعلی پرونده در نسخه پشتیبان تهیه می شود public/backups/fileName-history/ با زمان کنونی در میلی ثانیه به عنوان نام پرونده. همانطور که مشاهده خواهید کرد ، بعداً در قسمت جلویی از نام پرونده استفاده خواهیم کرد.

در این مثال ، ما هر وقت نسخه پشتیبان قبلی را ذخیره می کنیم ، جایگزین نسخه پشتیبان قبلی می شویم. چگونه می خواهید پشتیبان گیری بیشتری انجام دهید؟

واکشی پشتیبان ها

ما به روشی برای تهیه نسخه پشتیبان برای یک فایل خاص نیاز داریم ، بنابراین ما یک عدد را اضافه می کنیم /backups مسیر برای رسیدگی به این:

app.get("/backups", (req, res) => {
  const fileName = req.query.fileName;
  const backupsPath = path.join(__dirname, "public/backups", fileName + "-history");

  if (!fs.existsSync(backupsPath)) {
    return res.send([]);
  }

  const backupsPaths = fs.readdirSync(backupsPath);

  const fileNames = [];

  backupsPaths.forEach(filePath => {
    const fileName = path.basename(filePath);
    fileNames.push(fileName);
  });

  res.send(fileNames);
});

در اینجا ما از وجود پوشه پشتیبان برای آن پرونده اطمینان حاصل می کنیم و آرایه ای از همه پرونده های پشتیبان موجود در آن پوشه را برمی گردانیم. بله ، این امر به شما کمک می کند تا نسخه پشتیبان بیشتری را برای یک فایل نگه دارید. من نمی توانم تمام کارها را برای شما ادامه دهم!

باز کردن یک سند در مرورگر

خواهیم دید که چگونه می توانیم اسناد خود را برای ویرایش مستقیم در مرورگر با استفاده از OnlyOffice Docs باز کنیم.

باز کردن سند

ابتدا یک فایل HTML ساده ایجاد خواهیم کرد:

<!DOCTYPE html>
<html>

<head>
  <title>OnlyOffice Example</title>

  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
  <link rel="stylesheet" href="/public/css/main.css">
</head>

<body>
  <div id="placeholder"></div>
  <div id="documents">
    <h1>Documents</h1>
    <div id="document-controls">
      <div onclick="createDocument('.docx')">Create docx</div>
      <div onclick="createDocument('.xlsx')">Create xlsx</div>
      <div onclick="createDocument('.pptx')">Create pptx</div>
    </div>
  </div>
  <script type="text/javascript" src="http://localhost:8080/web-apps/apps/api/documents/api.js"></script>
  <script type="text/javascript" src="/public/javascript/main.js"></script>
</body>

</html>

همانطور که می بینید ، این پرونده چیز زیادی ندارد. ما … را داریم placeholder div که ویرایشگر پیوست خواهد شد. پس از آن وجود دارد documents div ، که شامل کنترل ها برای ایجاد اسناد و یک ظرف برای لیست نام فایل ها است.

در زیر آن ، اسکریپت با JavaScript API برای سرور اسناد داریم. بخاطر داشته باشید که ممکن است مجبور باشید مکان سرور Document خود را جایگزین کنید. اگر آن را با Docker من به شما دستور دادم ، شما باید خوب باشید

آخرین اما نه کمترین ، وجود دارد script برچسب ، جایی که ما JavaScript جلویی خود را وارد می کنیم ، و main.js پرونده ، جایی که دسترسی جهانی به آن خواهیم داشت DocsAPI هدف – شی.

CSS

قبل از اینکه به کدنویسی بپردازیم ، بیایید طرح را با برخی CSS جمع بندی کنیم تا برنامه ما بیشتر قابل استفاده و زشت باشد. موارد زیر را به اضافه کنید main.css:

html,
body {
  font-family: monospace;
  height: 100%;
  margin: 0;
  background-color: lavender;
  color: aliceblue;
}

h1 {
  color: lightslategray;
  display: inline-block;
}

#placeholder {
  height: 100%;
}

#documents {
  text-align: center;
}

#document-controls {
  text-align: center;
  margin: 5px;
}

#document-controls>div {
  display: inline-block;
  font-size: 15px;
  cursor: pointer;
  padding: 10px;
  background: mediumaquamarine;
}

#documents-list {
  padding: 5px;
  max-width: 400px;
  margin: auto;
}

.document {
  cursor: pointer;
  font-size: 20px;
  text-align: left;
  padding: 5px;
  margin: 2px;
  background-color: lightsteelblue;
}

.delete-doc {
  color: lightslategray;
  float: right;
  margin: 0 5px 0 5px;
}

نمایش اسناد موجود

با این کار ، ما آماده شروع کدگذاری قسمت جلویی هستیم. ما ابتدا با لیست کردن پرونده ها در documents پوشه برو به main.js و کد زیر را اضافه کنید:

const params = new URLSearchParams(window.location.search);
const fileName = params.get("fileName");

if (fileName) {
  editDocument(fileName);
} else {
  listDocuments();
}

function listDocuments() {
  
  document.getElementById("placeholder").style.display = "none";
  
  const oldList = document.getElementById("documents-list");
  oldList && oldList.remove();
  
  const documentsHtml = document.getElementById("documents");
  const docsListHtml = document.createElement("div");
  docsListHtml.id = "documents-list";

  documentsHtml.appendChild(docsListHtml);

  const req = new XMLHttpRequest();

  req.addEventListener("load", function (evt) {
    const docs = JSON.parse(this.response);

    docs.forEach(doc => {
      addDocumentHtml(doc);
    });
  });

  req.open("GET", "/documents");
  req.send();
}

function addDocumentHtml(fileName) {
  const docsListHtml = document.getElementById("documents-list");

  const docElement = document.createElement("div");
  docElement.id = fileName;
  docElement.textContent = fileName;
  docElement.setAttribute("class", "document");

  docElement.onclick = () => {
    openDocument(fileName);
  }

  const deleteElement = document.createElement("span");
  deleteElement.textContent = "X";
  deleteElement.setAttribute("class", "delete-doc");

  deleteElement.onclick = evt => {
    evt.stopPropagation();
    evt.preventDefault();
    deleteDocument(fileName);
  }

  docElement.appendChild(deleteElement);
  docsListHtml.appendChild(docElement);
}

function openDocument(fileName) {
  const url = "/?fileName=" + fileName;
  open(url, "_blank");
}

در اینجا در بالا ، ما پارامترهای جستجو را دریافت می کنیم تا بفهمیم آیا پرونده ای را باز می کنیم یا نه. اگر ما هستیم ، با editDocument تابع. نگران نباشید ، ما بعداً آن را ایجاد خواهیم کرد.

اگر پرونده ای را باز نمی کنیم ، می خواهیم لیستی از فایلهای موجود و کنترل ها را برای ایجاد موارد بیشتر نشان دهیم. که در listDocuments، ابتدا اطمینان حاصل می کنیم که موارد مخفی را پنهان می کنیم placeholder و لیست را پاک کنید تا مطمئن شوید که آن را از نو ایجاد کرده ایم. سپس ما تماس می گیریم /documents مسیری را که قبلاً ایجاد کردیم برای بدست آوردن همه پرونده ها ، تکرار آنها و ایجاد عناصر مربوطه. ما هر عنصر با نام پرونده را به عنوان ID شناسایی خواهیم کرد. به این ترتیب بعداً می توانیم به راحتی آنها را بازیابی کنیم.

توجه کنید که ما در حال تماس با addDocumentHtml عملکرد ، که بعداً برای افزودن پرونده های جدید مجدداً از آن استفاده خواهیم کرد.

برای هر یک از این اسناد ، ما همچنین تماس می گیریم openDocument، که در پایین تعریف کردیم و روی نماد صلیب آن را صدا می کنیم deleteDocument، که در ادامه تعریف خواهیم کرد.

حذف اسناد

برای حذف اسناد خود ، اگر کاربر مطمئن باشد قبل از اینکه ما تماس بگیریم و تماس بگیریم ، از او درخواست خواهیم کرد /delete مسیر را طی کنید و در آن پرونده هسته ای شوید. به جای هدر دادن تماس دیگر با API ، ما بررسی می کنیم که وضعیت برگشتی چیست 200 برای حذف مستقیم عناصر DOM:

function deleteDocument(fileName) {
  const canContinue = confirm("Are you sure you want to delete " + fileName + "?");

  if (!canContinue) {
    return;
  }

  const req = new XMLHttpRequest();

  req.addEventListener("load", function (evt) {
    if (this.status === 200) {
      return removeDocumentHtml(fileName);
    }

    alert("Could not delete " + fileName);
  });

  req.open("DELETE", "/delete?fileName=" + fileName);
  req.send();
}

function removeDocumentHtml(fileName) {
  const el = document.getElementById(fileName);
  el && el.remove();
}

اسناد ایجاد کنید

عملکردی را که با آن فراخوانی می کردیم به خاطر بسپارید onclick از کنترل های ایجاد سند؟ بفرمایید:

function createDocument(extension) {
  const name = prompt("What's the name of your new document?");
  const fileName = name + "." + extension;

  const req = new XMLHttpRequest();

  req.addEventListener("load", function (evt) {
    if (this.status === 200) {
      addDocumentHtml(fileName);
      return;
    }

    alert("Could not create " + fileName);
  });

  req.open("POST", "/create?fileName=" + fileName);
  req.send();
}

بسیار ساده. ما نام را اعلان می کنیم ، با /create مسیر با آن به عنوان fileName اگر پارامتر بازگردد ، 200 ما تماس می گیریم addDocumentHtml برای افزودن مستقیم عناصر DOM.

باز کردن اسناد در OnlyOffice Docs

حال باید تعریف کنیم editDocument تابع. کد زیر را به اضافه کنید main.js:

async function editDocument(fileName) {
  document.getElementById("documents").style.display = "none";

  const extension = fileName.substring(fileName.lastIndexOf(".") + 1);
  const documentType = getDocumentType(extension);
  const documentKey = await generateKey(fileName);

  console.log(documentKey);

  new DocsAPI.DocEditor("placeholder", {
    document: {
      fileType: extension,
      key: documentKey,
      title: fileName,
      url: "http://192.168.0.7:3000/documents/" + fileName,
    },
    documentType,
    editorConfig: {
      callbackUrl: "http://192.168.0.7:3000/track?fileName=" + fileName,
    },
    height: "100%",
    width: "100%",
  });
}

function generateKey(fileName) {
  return new Promise(resolve => {
    const req = new XMLHttpRequest();

    req.addEventListener("load", function (evt) {
      const backups = JSON.parse(this.response);
      const backupName = backups[0];
      const key = backupName ? backupName.substring(0, backupName.indexOf(".")) : new Date().getTime();
      resolve(String(key));
    });

    req.open("GET", "/backups?fileName=" + fileName);
    req.send();
  });
}

function getDocumentType(extension) {
  const documentTypes = {
    text: ["doc", "docx", "docm", "dot", "dotx", "dotm", "odt", "fodt", "ott", "rtf", "txt", "html", "htm", "mht", "pdf", "djvu", "fb2", "epub", "xps"],
    spreadsheet: ["xls", "xlsx", "xlsm", "xlt", "xltx", "xltm", "ods", "fods", "ots", "csv"],
    presentation: ["pps", "ppsx", "ppsm", "ppt", "pptx", "pptm", "pot", "potx", "potm", "odp", "fodp", "otp"],
  }

  if (documentTypes.text.indexOf(extension) >= 0) {
    return "text";
  }
  if (documentTypes.spreadsheet.indexOf(extension) >= 0) {
    return "spreadsheet";
  }
  if (documentTypes.presentation.indexOf(extension) >= 0) {
    return "presentation";
  }
}

بنابراین ، ما سه عملکرد اضافه کرده ایم. بیایید ابتدا روی دو مورد آخر تمرکز کنیم. (ما در مورد editDocument در یک لحظه.)

generateKey همچنین با تولید کلید به ما کمک خواهد کرد. این یک شناسه مستند منحصر به فرد است که برای شناسایی اسناد توسط سرویس استفاده می شود. حداکثر طول آن 20 و بدون نویسه خاص است. و این ترفند این است: هر بار که سند ذخیره می شود ، باید دوباره تولید شود. می بینی این کجا می رود؟ دقیقا! ما برای تولید کلیدهای خود از نام پرونده های پشتیبان خود سود خواهیم برد.

همانطور که مشاهده می کنید ، برای تولید کلید ما تنها نسخه پشتیبان خود را بازیابی می کنیم (در صورت وجود) و با استفاده از نام آن یا در غیر اینصورت ساده می توان زمان فعلی را در صورت عدم وجود در میلی ثانیه دریافت کرد.

اگر بخواهید از نسخه پشتیبان تهیه کنید ، چه عواملی باید در این عملکرد تغییر کند؟ [Runs away]

getDocumentType باز خواهد گشت text، spreadsheet یا presentation. OnlyOffice به این نیاز دارد تا بداند کدام ویرایشگر را باز کند.

editDocument چیزی است که ما برای آن هستیم این همان چیزی است که شما در تمام مدت منتظر آن بوده اید. در اینجا ما نمونه را نشان می دهیم DocEditor گذراندن شناسه شناسنامه ما placeholder div و یک شی با تعدادی پیکربندی.

پیکربندی DocEditor

آنچه تاکنون به شما نشان داده ام حداقل گزینه های مورد نیاز برای نمونه سازی است DocEditor. شما باید بررسی کنید پارامترهای پیشرفته بخش در اسناد را ببینید تا ببینید چگونه می توانید از همه گزینه های مختلف سود ببرید. در همین حال ، بگذارید شما را از اصول جستجو کنم.

در بالا ، ما سند فیلدی که یک شی شامل اطلاعات مربوط به سندی را که می خواهیم باز کنیم ، می گیرد.

پس ما داریم documentType، که همانطور که قبلاً دیدیم ، می تواند هر دو باشد text، spreadsheet، یا presentation.

درست در زیر آن قرار دارد editorConfig شی object ، که به شما امکان می دهد مواردی از قبیل را تنظیم کنید spellcheck، unit و zoom، در میان چیز های دیگر. در این حالت ، ما فقط از callbackUrl، که URL به /track مسیری که Document Server از آن برای ذخیره فایل استفاده می کند.

نتیجه

ما به پایان رسیده ایم ، و شما امیدوارید که نحوه تنظیم و ادغام OnlyOffice Docs با برنامه وب خود را فرا گرفته اید. چیزهای زیادی وجود دارد که ما آنها را کنار می گذاریم ، مانند مجوزها، به اشتراک گذاری، سفارشی سازی و بسیاری از کارهای دیگر که می توانید با OnlyOffice انجام دهید.

امیدوارم شما بتوانید اطلاعات کافی برای بهبود محصول خود یا حتی حتی الهام گرفتن از شروع یک پروژه جدید را از ابتدا دریافت کرده باشید. هیچ وقت مثل زمان حال وجود ندارد.

خوب ، در مورد بعدی شما را می بینم. در ضمن ، کد نویسی را ادامه دهید و به یاد داشته باشید که وقتی در آن هستید از آن لذت ببرید!