HTML পরিষেবা: টেমপ্লেটেড HTML

আপনি অ্যাপস স্ক্রিপ্ট কোড এবং HTML মিশ্রিত করে খুব কম পরিশ্রমে ডায়নামিক পৃষ্ঠা তৈরি করতে পারেন। যদি আপনি এমন কোনও টেমপ্লেটিং ভাষা ব্যবহার করেন যা কোড এবং HTML মিশ্রিত করে, যেমন PHP, ASP, অথবা JSP, তাহলে বাক্য গঠনটি পরিচিত মনে হবে।

স্ক্রিপ্টলেট

অ্যাপস স্ক্রিপ্ট টেমপ্লেটগুলিতে তিনটি বিশেষ ট্যাগ থাকতে পারে, যাকে স্ক্রিপ্টলেট বলা হয়। একটি স্ক্রিপ্টলেটের ভিতরে, আপনি যে কোনও কোড লিখতে পারেন যা একটি সাধারণ অ্যাপস স্ক্রিপ্ট ফাইলে কাজ করবে: স্ক্রিপ্টলেটগুলি অন্যান্য কোড ফাইলে সংজ্ঞায়িত ফাংশনগুলিকে কল করতে পারে, গ্লোবাল ভেরিয়েবলগুলি উল্লেখ করতে পারে, অথবা যেকোনো অ্যাপস স্ক্রিপ্ট API ব্যবহার করতে পারে। আপনি এমনকি স্ক্রিপ্টলেটের মধ্যে ফাংশন এবং ভেরিয়েবলগুলিকে সংজ্ঞায়িত করতে পারেন, এই সতর্কতা সহ যে কোড ফাইল বা অন্যান্য টেমপ্লেটে সংজ্ঞায়িত ফাংশন দ্বারা সেগুলিকে কল করা যাবে না।

নিচের উদাহরণটি যদি আপনি স্ক্রিপ্ট এডিটরে পেস্ট করেন, <?= ... ?> ট্যাগের (একটি প্রিন্টিং স্ক্রিপ্টলেট ) বিষয়বস্তু তির্যকভাবে প্রদর্শিত হবে। পৃষ্ঠাটি ব্যবহারকারীকে পরিবেশন করার আগে সেই তির্যক কোডটি সার্ভারে চলে। যেহেতু পৃষ্ঠাটি পরিবেশন করার আগে scriptlet কোড কার্যকর হয়, তাই এটি প্রতি পৃষ্ঠায় কেবল একবার চালানো যেতে পারে; ক্লায়েন্ট-সাইড জাভাস্ক্রিপ্ট বা অ্যাপস স্ক্রিপ্ট ফাংশনগুলির বিপরীতে যা আপনি google.script.run এর মাধ্যমে কল করেন, পৃষ্ঠা লোড হওয়ার পরে scriptlets আবার কার্যকর করতে পারে না।

কোড.জিএস

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

ইনডেক্স.এইচটিএমএল

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    Hello, World! The time is <?= new Date() ?>.
  </body>
</html>

মনে রাখবেন যে টেমপ্লেট করা HTML এর জন্য doGet() ফাংশনটি মৌলিক HTML তৈরি এবং পরিবেশনের উদাহরণ থেকে আলাদা। এখানে দেখানো ফাংশনটি HTML ফাইল থেকে একটি HtmlTemplate অবজেক্ট তৈরি করে, তারপর স্ক্রিপ্টলেটগুলি কার্যকর করার জন্য তার evaluate() পদ্ধতিটি কল করে এবং টেমপ্লেটটিকে একটি HtmlOutput অবজেক্টে রূপান্তর করে যা স্ক্রিপ্টটি ব্যবহারকারীকে পরিবেশন করতে পারে।

স্ট্যান্ডার্ড স্ক্রিপ্টলেট

স্ট্যান্ডার্ড স্ক্রিপ্টলেট, যা <? ... ?> সিনট্যাক্স ব্যবহার করে, পৃষ্ঠায় স্পষ্টভাবে কন্টেন্ট আউটপুট না করেই কোড এক্সিকিউট করে। যাইহোক, এই উদাহরণে দেখা যাচ্ছে যে, স্ক্রিপ্টলেটের ভিতরে থাকা কোডের ফলাফল স্ক্রিপ্টলেটের বাইরের HTML কন্টেন্টকে প্রভাবিত করতে পারে:

কোড.জিএস

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

ইনডেক্স.এইচটিএমএল

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? if (true) { ?>
      <p>This will always be served!</p>
    <? } else  { ?>
      <p>This will never be served.</p>
    <? } ?>
  </body>
</html>

স্ক্রিপ্টলেট মুদ্রণ করা হচ্ছে

<?= ... ?> সিনট্যাক্স ব্যবহার করে এমন স্ক্রিপ্টলেট প্রিন্ট করার সময়, তাদের কোডের ফলাফল কনটেক্সচুয়াল এস্কেপিং ব্যবহার করে পৃষ্ঠায় আউটপুট করা হয়।

কনটেক্সচুয়াল এস্কেপিং এর অর্থ হল অ্যাপস স্ক্রিপ্ট পৃষ্ঠায় আউটপুটের প্রেক্ষাপট ট্র্যাক করে — একটি HTML অ্যাট্রিবিউটের ভিতরে, একটি ক্লায়েন্ট-সাইড script ট্যাগের ভিতরে, অথবা অন্য কোথাও — এবং ক্রস-সাইট স্ক্রিপ্টিং (XSS) আক্রমণ থেকে রক্ষা করার জন্য স্বয়ংক্রিয়ভাবে এস্কেপ অক্ষর যোগ করে।

এই উদাহরণে, প্রথম প্রিন্টিং স্ক্রিপ্টলেটটি সরাসরি একটি স্ট্রিং আউটপুট করে; এর পরে একটি স্ট্যান্ডার্ড স্ক্রিপ্টলেট আসে যা একটি অ্যারে এবং একটি লুপ সেট আপ করে, তারপরে আরেকটি প্রিন্টিং স্ক্রিপ্টলেট অ্যারের বিষয়বস্তু আউটপুট করে।

কোড.জিএস

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

ইনডেক্স.এইচটিএমএল

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <?= 'My favorite Google products:' ?>
    <? var data = ['Gmail', 'Docs', 'Android'];
      for (var i = 0; i < data.length; i++) { ?>
        <b><?= data[i] ?></b>
    <? } ?>
  </body>
</html>

মনে রাখবেন যে একটি প্রিন্টিং স্ক্রিপ্টলেট শুধুমাত্র তার প্রথম স্টেটমেন্টের মান আউটপুট করে; বাকি যেকোনো স্টেটমেন্ট এমনভাবে আচরণ করে যেন সেগুলি একটি স্ট্যান্ডার্ড স্ক্রিপ্টলেটে রয়েছে। সুতরাং, উদাহরণস্বরূপ, স্ক্রিপ্টলেট <?= 'Hello, world!'; 'abc' ?> শুধুমাত্র "Hello, world!" প্রিন্ট করে।

জোর করে স্ক্রিপ্টলেট মুদ্রণ করা

ফোর্স-প্রিন্টিং স্ক্রিপ্টলেট, যা <?!= ... ?> সিনট্যাক্স ব্যবহার করে, স্ক্রিপ্টলেট প্রিন্ট করার মতো, তবে তারা প্রাসঙ্গিক এস্কেপিং এড়ায়।

যদি আপনার স্ক্রিপ্ট অবিশ্বস্ত ব্যবহারকারীর ইনপুট অনুমোদন করে তবে কনটেক্সচুয়াল এস্কেপিং গুরুত্বপূর্ণ। বিপরীতে, যদি আপনার স্ক্রিপ্টলেটের আউটপুটে ইচ্ছাকৃতভাবে HTML বা স্ক্রিপ্ট থাকে যা আপনি ঠিক যেমনটি নির্দিষ্টভাবে সন্নিবেশ করতে চান তবে আপনাকে জোর করে প্রিন্ট করতে হবে।

সাধারণ নিয়ম হিসেবে, জোর করে স্ক্রিপ্টলেট প্রিন্ট করার পরিবর্তে প্রিন্টিং স্ক্রিপ্টলেট ব্যবহার করুন, যদি না আপনি জানেন যে আপনাকে HTML বা জাভাস্ক্রিপ্ট অপরিবর্তিত রেখে প্রিন্ট করতে হবে।

স্ক্রিপ্টলেটে অ্যাপস স্ক্রিপ্ট কোড

স্ক্রিপ্টলেটগুলি কেবল সাধারণ জাভাস্ক্রিপ্ট চালানোর মধ্যেই সীমাবদ্ধ নয়; আপনি আপনার টেমপ্লেটগুলিকে অ্যাপস স্ক্রিপ্ট ডেটাতে অ্যাক্সেস দেওয়ার জন্য নিম্নলিখিত তিনটি কৌশল ব্যবহার করতে পারেন।

তবে মনে রাখবেন, যেহেতু টেমপ্লেট কোডটি ব্যবহারকারীকে পৃষ্ঠাটি পরিবেশন করার আগে কার্যকর হয়, তাই এই কৌশলগুলি কেবল একটি পৃষ্ঠায় প্রাথমিক সামগ্রী ফিড করতে পারে। ইন্টারেক্টিভভাবে একটি পৃষ্ঠা থেকে অ্যাপস স্ক্রিপ্ট ডেটা অ্যাক্সেস করতে, পরিবর্তে google.script.run API ব্যবহার করুন।

একটি টেমপ্লেট থেকে অ্যাপস স্ক্রিপ্ট ফাংশন কল করা

স্ক্রিপ্টলেটগুলি অ্যাপস স্ক্রিপ্ট কোড ফাইল বা লাইব্রেরিতে সংজ্ঞায়িত যেকোনো ফাংশনকে কল করতে পারে। এই উদাহরণে স্প্রেডশিট থেকে ডেটা টেমপ্লেটে টেনে আনার এবং তারপর ডেটা থেকে একটি HTML টেবিল তৈরি করার একটি উপায় দেখানো হয়েছে।

কোড.জিএস

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

function getData() {
  return SpreadsheetApp
      .openById('1234567890abcdefghijklmnopqrstuvwxyz')
      .getActiveSheet()
      .getDataRange()
      .getValues();
}

ইনডেক্স.এইচটিএমএল

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? var data = getData(); ?>
    <table>
      <? for (var i = 0; i < data.length; i++) { ?>
        <tr>
          <? for (var j = 0; j < data[i].length; j++) { ?>
            <td><?= data[i][j] ?></td>
          <? } ?>
        </tr>
      <? } ?>
    </table>
  </body>
</html>

অ্যাপস স্ক্রিপ্ট এপিআইগুলিকে সরাসরি কল করা

আপনি স্ক্রিপ্টলেটে সরাসরি অ্যাপস স্ক্রিপ্ট কোড ব্যবহার করতে পারেন। এই উদাহরণটি একটি পৃথক ফাংশনের পরিবর্তে টেমপ্লেটেই ডেটা লোড করে পূর্ববর্তী উদাহরণের মতো একই ফলাফল অর্জন করে।

কোড.জিএস

function doGet() {
  return HtmlService
      .createTemplateFromFile('Index')
      .evaluate();
}

ইনডেক্স.এইচটিএমএল

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? var data = SpreadsheetApp
        .openById('1234567890abcdefghijklmnopqrstuvwxyz')
        .getActiveSheet()
        .getDataRange()
        .getValues(); ?>
    <table>
      <? for (var i = 0; i < data.length; i++) { ?>
        <tr>
          <? for (var j = 0; j < data[i].length; j++) { ?>
            <td><?= data[i][j] ?></td>
          <? } ?>
        </tr>
      <? } ?>
    </table>
  </body>
</html>

টেমপ্লেটে ভেরিয়েবল পুশ করা হচ্ছে

পরিশেষে, আপনি HtmlTemplate অবজেক্টের বৈশিষ্ট্য হিসেবে ভেরিয়েবলগুলিকে একটি টেমপ্লেটে পুশ করতে পারেন। আবারও, এই উদাহরণটি পূর্ববর্তী উদাহরণগুলির মতো একই ফলাফল অর্জন করে।

কোড.জিএস

function doGet() {
  var t = HtmlService.createTemplateFromFile('Index');
  t.data = SpreadsheetApp
      .openById('1234567890abcdefghijklmnopqrstuvwxyz')
      .getActiveSheet()
      .getDataRange()
      .getValues();
  return t.evaluate();
}

ইনডেক্স.এইচটিএমএল

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <table>
      <? for (var i = 0; i < data.length; i++) { ?>
        <tr>
          <? for (var j = 0; j < data[i].length; j++) { ?>
            <td><?= data[i][j] ?></td>
          <? } ?>
        </tr>
      <? } ?>
    </table>
  </body>
</html>

টেমপ্লেট ডিবাগ করা হচ্ছে

টেমপ্লেট ডিবাগ করা কঠিন হতে পারে কারণ আপনার লেখা কোডটি সরাসরি কার্যকর করা হয় না; পরিবর্তে, সার্ভার আপনার টেমপ্লেটটিকে কোডে রূপান্তরিত করে, তারপর সেই ফলাফলযুক্ত কোডটি কার্যকর করে।

যদি টেমপ্লেটটি আপনার স্ক্রিপ্টলেটগুলিকে কীভাবে ব্যাখ্যা করছে তা স্পষ্ট না হয়, তাহলে HtmlTemplate ক্লাসের দুটি ডিবাগিং পদ্ধতি আপনাকে কী ঘটছে তা আরও ভালভাবে বুঝতে সাহায্য করতে পারে।

কোড পান()

getCode() একটি স্ট্রিং ফেরত দেয় যেখানে সার্ভার টেমপ্লেট থেকে তৈরি কোডটি থাকে। যদি আপনি কোডটি লগ করেন , তারপর স্ক্রিপ্ট এডিটরে পেস্ট করেন, তাহলে আপনি এটি চালাতে এবং সাধারণ অ্যাপস স্ক্রিপ্ট কোডের মতো ডিবাগ করতে পারবেন।

এখানে একটি সহজ টেমপ্লেট যা আবার Google পণ্যের তালিকা প্রদর্শন করে, তারপরে getCode() এর ফলাফল দেখায়:

কোড.জিএস

function myFunction() {
  Logger.log(HtmlService
      .createTemplateFromFile('Index')
      .getCode());
}

ইনডেক্স.এইচটিএমএল

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <?= 'My favorite Google products:' ?>
    <? var data = ['Gmail', 'Docs', 'Android'];
      for (var i = 0; i < data.length; i++) { ?>
        <b><?= data[i] ?></b>
    <? } ?>
  </body>
</html>

লগ (মূল্যায়ন করা হয়েছে)

(function() { var output = HtmlService.initTemplate(); output._ =  '<!DOCTYPE html>\n';
  output._ =  '<html>\n' +
    '  <head>\n' +
    '    <base target=\"_top\">\n' +
    '  </head>\n' +
    '  <body>\n' +
    '    '; output._$ =  'My favorite Google products:' ;
  output._ =  '    ';  var data = ['Gmail', 'Docs', 'Android'];
        for (var i = 0; i < data.length; i++) { ;
  output._ =  '        <b>'; output._$ =  data[i] ; output._ =  '</b>\n';
  output._ =  '    ';  } ;
  output._ =  '  </body>\n';
  output._ =  '</html>';
  /* End of user code */
  return output.$out.append('');
})();

মন্তব্যের সাথে কোড পান()

getCodeWithComments() getCode() এর অনুরূপ, কিন্তু মূল্যায়ন করা কোডটিকে মন্তব্য হিসেবে ফেরত দেয় যা মূল টেমপ্লেটের পাশাপাশি প্রদর্শিত হয়।

মূল্যায়ন করা কোডের মধ্য দিয়ে হেঁটে যাওয়া

মূল্যায়ন করা কোডের যেকোনো নমুনায় আপনি প্রথমেই যে জিনিসটি লক্ষ্য করবেন তা হল HtmlService.initTemplate() পদ্ধতি দ্বারা তৈরি অন্তর্নিহিত output অবজেক্ট। এই পদ্ধতিটি নথিভুক্ত নয় কারণ শুধুমাত্র টেমপ্লেটগুলিকেই এটি ব্যবহার করতে হয়। output হল একটি বিশেষ HtmlOutput অবজেক্ট যার দুটি অস্বাভাবিক নামযুক্ত বৈশিষ্ট্য রয়েছে, _ এবং _$ , যা append() এবং appendUntrusted() কল করার জন্য সংক্ষিপ্ত।

output আরও একটি বিশেষ বৈশিষ্ট্য আছে, $out , যা একটি নিয়মিত HtmlOutput অবজেক্টকে বোঝায় যার এই বিশেষ বৈশিষ্ট্যগুলি নেই। টেমপ্লেটটি কোডের শেষে সেই স্বাভাবিক অবজেক্টটি ফেরত দেয়।

এখন যেহেতু আপনি এই সিনট্যাক্সটি বুঝতে পেরেছেন, বাকি কোডটি অনুসরণ করা মোটামুটি সহজ হবে। স্ক্রিপ্টলেটের বাইরে HTML কন্টেন্ট (যেমন b ট্যাগ) output._ = ( contextual escaping ছাড়া) ব্যবহার করে যুক্ত করা হয়, এবং স্ক্রিপ্টলেটগুলি জাভাস্ক্রিপ্ট হিসাবে যুক্ত করা হয় (স্ক্রিপ্টলেটের ধরণের উপর নির্ভর করে কনটেক্সচুয়াল escaping সহ বা ছাড়াই)।

মনে রাখবেন যে মূল্যায়ন করা কোডটি টেমপ্লেট থেকে লাইন নম্বরগুলি সংরক্ষণ করে। মূল্যায়ন করা কোডটি চালানোর সময় যদি আপনি কোনও ত্রুটি পান, তাহলে লাইনটি টেমপ্লেটের সমতুল্য বিষয়বস্তুর সাথে সঙ্গতিপূর্ণ হবে।

মন্তব্যের শ্রেণিবিন্যাস

যেহেতু মূল্যায়ন করা কোড লাইন নম্বর সংরক্ষণ করে, তাই স্ক্রিপ্টলেটের ভিতরের মন্তব্যগুলি অন্যান্য স্ক্রিপ্টলেট এবং এমনকি HTML কোডকেও মন্তব্য করতে পারে। এই উদাহরণগুলি মন্তব্যের কিছু আশ্চর্যজনক প্রভাব দেখায়:

<? var x; // a comment ?> This sentence won't print because a comment begins inside a scriptlet on the same line.

<? var y; // ?> <?= "This sentence won't print because a comment begins inside a scriptlet on the same line.";
output.append("This sentence will print because it's on the next line, even though it's in the same scriptlet.”) ?>

<? doSomething(); /* ?>
This entire block is commented out,
even if you add a */ in the HTML
or in a <script> */ </script> tag,
<? until you end the comment inside a scriptlet. */ ?>