Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.
HTML
  <!DOCTYPE html>
  <html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Release Notes</title>
  </head>
  <body style="margin:0; padding:0; font-family: Arial, Helvetica, sans-serif; background:#ffffff;">
    <div style="max-width:1200px; margin:0 auto; padding:16px;">
      <div style="border:1px solid #dfe1e6; border-radius:8px; overflow:hidden;">
        <div style="background:#0052CC; color:#fff; padding:14px 16px;">
          <div style="display:flex; align-items:center; gap:12px;">
            <!-- TOTVS logo (anexo do Confluence/TDN):
                - Nome do anexo: "totvs logo.jpg"
                - O JS abaixo tenta carregar primeiro do pageId atual, e cai em fallback se não achar -->
            <img
              id="totvs-logo"
              alt="TOTVS"
              style="display:none; height:28px; width:auto; background:#ffffff; border-radius:6px; padding:4px; border:1px solid rgba(255,255,255,0.35);"
            />
            <div id="page-title" style="font-size:18px; font-weight:700;">Dicionário de Dados</div>
          </div>
        </div>

        <div style="background:#f4f5f7; border-top:1px solid #dfe1e6; padding:12px 16px; display:flex; gap:12px; align-items:center; flex-wrap:wrap;">
          <div style="display:flex; gap:8px; align-items:center;">
            <div style="font-weight:700; color:#172b4d;">Versão:</div>
            <div id="version-display" style="background:#fff; border:1px solid #dfe1e6; border-radius:6px; padding:4px 10px; font-weight:700; color:#172b4d;">-</div>
          </div>

          <div style="display:flex; gap:8px; align-items:center;">
            <div style="font-weight:700; color:#172b4d;">Atualizado:</div>
            <div id="last-update" style="background:#fff; border:1px solid #dfe1e6; border-radius:6px; padding:4px 10px; color:#172b4d;">-</div>
          </div>

          <a id="doc-link" href="#" target="_blank" style="display:none; margin-left:auto; text-decoration:none; background:#ffffff; color:#0052CC; border:1px solid #0052CC; border-radius:6px; padding:6px 10px; font-weight:700;">
            Abrir no Drive
          </a>
        </div>

        <div id="widget-container" style="min-height:520px; background:#ffffff;">
          <div style="padding:18px; color:#6b778c;">Carregando documento...</div>
        </div>
      </div>
    </div>

    <script>
    (function() {
      // MapeamentoFonte: de versões para arquivos no Drive (Google Docs ou .txt)
GitHub (RAW)
      // Repo: https://github.com/Juansimeoni/ReleaseDatasul
      // Pasta: 141aJRhZeYZca4QCBnou6-00WzRLkBYbsArquivo esperado: {versao}.txt (ex: 12.1.2503.15.txt)
    // Data: 2025-12-17 09:03:10
    // Total: 79 versões
    const versionToFile = { const GITHUB_OWNER = 'Juansimeoni';
      const GITHUB_REPO = 'ReleaseDatasul';
    '12.1.2503.14': { id: '1pXbF0em-a6e0GYqOMNcbHPGpOOM09VX0', type: 'txt', bytes: 13680 },
    '12.1.2503.15': { id: '1yW4jIfKG9EL9Rfjfxpc3HylEVe1LOrThr4-oxSv21ow', type: 'txt', bytes: 0 },
    '12.1.2503.16': { id: '13mOmXGZ9HaZob2Df6t27osRxWfV138_v', type: 'txt', bytes: 0 },
    '12.1.2507': { id: '1WdEk0hszYS2UDuOM9vE1_KngjkH74oxR', type: 'txt', bytes: 0 },
    '12.1.2507.1': { id: '1AyMLAxejP2heuLdXWjHKXeWAkSPkVB0f', type: 'txt', bytes: 0 },
    '12.1.2507.2': { id: '1OA878i2Izd3BiwZ0m1UR_mEXgCoxZOyh', type: 'txt', bytes: 0 },
    '12.1.2507.3': { id: '133AZhy4WixraizeKsqSf6DvJLXyt3h2c', type: 'txt', bytes: 0 },
    '12.1.2507.4': { id: '1n7D6J02XoWPOQzIj9sPXKo9FTdiKgFcL', type: 'txt', bytes: 0 },
    '12.1.2507.5': { id: '1TjukYrLbIi1Uc6adUTLrIvwSP-7LLv7V', type: 'txt', bytes: 0 },
    '12.1.2507.6': { id: '16HpmgtvM0WhxN8sr-PiMwNzzKDLocaz1', type: 'txt', bytes: 0 },
    '12.1.2507.7': { id: '1iHJlFXGQMyS-qq6FMj-Ip4n2xLrMjtZA', type: 'txt', bytes: 0 },
    '12.1.2507.8': { id: '1EzYgb0Cvha1FB5iX5pZ8I5nh4M8gFnxM', type: 'txt', bytes: 0 },
    '12.1.2507.9': { id: '1rr_KlDuyyZpKlrOJeaXN-xy3AKlx-Cbu', type: 'txt', bytes: 0 },
    '12.1.2507.10': { id: '1pptApgNcLAhk5fNIgq8a-onq-vSNWs3n', type: 'txt', bytes: 0 },
    '12.1.2507.11': { id: '1STmDXQJDoOH5xMx3qzOd44c-t_k1y7jR', type: 'txt', bytes: 0 },
    '12.1.2507.12': { id: '1BRHWVWAIlhWbTqyIui7VXcqvvLm7l33D', type: 'txt', bytes: 0 },
    '12.1.2507.13': { id: '19aARCX-cSPA9QIeAxZIW5XxPNXZ3DrMr', type: 'txt', bytes: 0 },
    '12.1.2507.14': { id: '1Kb0f1ijpnPyFLdkHKzHadRvS3FenqIqL', type: 'txt', bytes: 0 },
    '12.1.2511': { id: '14k_1KZwT66qJGnV6mbnEw3bjV-YW7e24', type: 'txt', bytes: 0 },
    '12.1.2511.1': { id: '1OvmZgFOtOoWNVk2zyq1XhFNibCz6pJEc', type: 'txt', bytes: 0 },
    '12.1.2511.2': { id: '1rV53pygU6L6Ms6x3TU6-syUVsoF6bRs7', type: 'txt', bytes: 0 },
    '12.1.2511.3': { id: '1WxDLhvrFSwdV2Fj1IDs_VHB6n7KSog8O', type: 'txt', bytes: 0 },
    '12.1.2511.4': { id: '1lM65BjCZEzqWcTuP2E9aWaKvORK_nxL5', type: 'txt', bytes: 0 },
    '12.1.2511.5': { id: '1SiFQs7kQFV9lgodbrigees8erNiSg54P', type: 'txt', bytes: 0 },
    '12.1.2511.6': { id: '1mhhHU3KgpUKHVZ6JFFY2qdDkKYF1_uQ5', type: 'txt', bytes: 0 },
    '12.1.2511.7': { id: '1yHtTXT2B0ZiTRHnBdbkqQkyEm0epeKhT', type: 'txt', bytes: 0 },
    '12.1.2511.8': { id: '1gx-1Al9ZOW6jUTwxWrp9HciFn5jZi3d8', type: 'txt', bytes: 0 },
    '12.1.2603': { id: '1VavQf3Ydp_ig_LlgUU-qSgKchZNuMB6P', type: 'txt', bytes: 0 },
    '12.2.2411': { id: '1WyGG5rBNM2Uoy_j1W9DyrwFC3Oj63CJx', type: 'txt', bytes: 0 },
    '12.2.2411.16': { id: '121oW6Mq_ivk2lT9wfORwVhpmkTk84lWL', type: 'txt', bytes: 0 },
    '12.2.2411.17': { id: '1P8o3z3encE-72SlMTylf7t0rovGZVOJMCGado1To4Ok', type: 'doc' },
    '12.2.2503': { id: '1cIup3Z-F7J4NE9MMoFUj7CLcbpmLOQP6', type: 'txt', bytes: 0 },
    '12.2.2503.11': { id: '1mfmpLR7h0ruGo397tQCtHpYcSCwpqn4N', type: 'txt', bytes: 0 },
    '12.2.2503.12': { id: '1O_-1CnuKhm1XAtnVMmsb9ovSBVGWA-6G', type: 'txt', bytes: 0 },
    '12.2.2503.13': { id: '1bwgAgWYzWRFn2E8L_2Y_xokPepLEIF6_', type: 'txt', bytes: 0 },
    '12.2.2503.15': { id: '1vIy-eE4UtDvI0Vv7BXPZvyucO2su6KkL', type: 'txt', bytes: 0 },
    '12.2.2507': { id: '1OGqnrdwMc0j6zwaRhMPNGG8bkuO-kECb', type: 'txt', bytes: 0 },
    '12.2.2507.4': { id: '1I2PJINB0z4HWlcxDTiq-I9skmO0A9zc8', type: 'txt', bytes: 0 },
    '12.2.2507.5': { id: '19kiRv3tBFyUSHnGwyvjvGMBhC5EzToXx', type: 'txt', bytes: 0 },
    '12.2.2507.6': { id: '1L_Jw3GMjz7F_OaNZr_lzcJHRKGW76gbN', type: 'txt', bytes: 0 },
    '12.2.2507.7': { id: '1ZDyVikOavwwz5rCByx1_s9PyF2czY_BF', type: 'txt', bytes: 0 },
    '12.2.2507.8': { id: '1cLJ_BPltcqHslEP1Lim0iUMUBPBQrYPz', type: 'txt', bytes: 0 },
    '12.2.2507.9': { id: '13Er9lyxm4HmPPCd3JNymTvX_Dl4-mdlj', type: 'txt', bytes: 0 },
    '12.2.2511': { id: '1eGYnJ1I820VGvUwrpuMvWCWqlgiiWvkg', type: 'txt', bytes: 0 },
    '12.2.2511.1': { id: '1jIW7ElN9zpUw4B-9z98O1pkpyKWXS9vW', type: 'txt', bytes: 0 },
    '12.2.2511.2': { id: '1MCI-twulc3CVz8rfeoJNF-gXrx_B_QBy', type: 'txt', bytes: 0 },
    '12.2.2511.3': { id: '1u49828qMI0OtX129cMPD-zysUGof4msV', type: 'txt', bytes: 0 },
};

    function getPageVersion() {
      const versionPattern = /(\d+\.\d+\.\d+(?:\.\d+)?)/;

      const pageTitleElement = document.querySelector('h1#title-text, .page-title, h1, [data-page-title]');
      if (pageTitleElement) {
        const titleText = (pageTitleElement.textContent || '').trim();
        const match = titleText.match(versionPattern);
        if (match) return match[1];
      }

      const metaTitle = document.querySelector('meta[property="og:title"], meta[name="title"]');
      if (metaTitle) {const GITHUB_BRANCH = 'main';
      const GITHUB_PATH_PREFIX = ''; // ex: 'releases' (sem / no início e no fim)

      const GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/' + GITHUB_OWNER + '/' + GITHUB_REPO + '/' + GITHUB_BRANCH + '/';
      const GITHUB_BLOB_BASE = 'https://github.com/' + GITHUB_OWNER + '/' + GITHUB_REPO + '/blob/' + GITHUB_BRANCH + '/';
      const GITHUB_API_BASE = 'https://api.github.com/repos/' + GITHUB_OWNER + '/' + GITHUB_REPO + '/';

      function buildRepoPath(version) {
        const filename = version + '.txt';
        return GITHUB_PATH_PREFIX ? (GITHUB_PATH_PREFIX + '/' + filename) : filename;
      }

      function setLastUpdateText(text) {
        const el = document.getElementById('last-update');
        if (el) el.textContent = text || '-';
      }

      function formatDatePtBr(value) {
        try {
          const d = new Date(value);
          if (isNaN(d.getTime())) return null;
          return d.toLocaleString('pt-BR');
        } catch (e) {
          return null;
        }
      }

      function tryFetchCommitDate(pathInRepo) {
        // Busca o último commit do arquivo (public repo, sem token)
        // Endpoint: /commits?path=...&sha=...&per_page=1
        const url = GITHUB_API_BASE + 'commits?path=' + encodeURIComponent(pathInRepo) +
          '&sha=' + encodeURIComponent(GITHUB_BRANCH) + '&per_page=1';

        return fetch(url, { cache: 'no-store' })
          .then(function(resp) {
            if (!resp.ok) return null;
            return resp.json();
          })
          .then(function(arr) {
            if (!arr || !arr.length) return null;
            const c = arr[0] || {};
            const commit = c.commit || {};
            const committer = commit.committer || {};
            const author = commit.author || {};
            // prefer committer date, fallback author date
            return committer.date || author.date || null;
          })
          .catch(function() {
            return null;
          });
      }

      function getPageVersion() {
        const versionPattern = /(\d+\.\d+\.\d+(?:\.\d+)?)/;

        const pageTitleElement = document.querySelector('h1#title-text, .page-title, h1, [data-page-title]');
        if (pageTitleElement) {
          const titleText = (pageTitleElement.textContent || '').trim();
          const match = titleText.match(versionPattern);
          if (match) return match[1];
        }

        const metaTitle = document.querySelector('meta[property="og:title"], meta[name="title"]');
        if (metaTitle) {
          const titleText = metaTitle.getAttribute('content') || '';
          const match = titleText.match(versionPattern);
          if (match) return match[1];
        }

        const titleMatch = (document.title || '').match(versionPattern);
        if (titleMatch) return titleMatch[1];

        return null;
      }

      function getPageId() {
        try {
          const qs = new URLSearchParams(window.location.search);
          const fromQuery = qs.get('pageId');
          if (fromQuery) return String(fromQuery);
        } catch (e) { /* ignore */ }

        // Confluence costuma expor isso quando AJS está disponível
        try {
          if (window.AJS && AJS.params && AJS.params.pageId) return String(AJS.params.pageId);
        } catch (e) { /* ignore */ }

        return null;
      }

      function setTotvsLogo() {
        const logoEl = document.getElementById('totvs-logo');
        if (!logoEl) return;

        const pageId = getPageId();
        const filename = 'totvs logo.jpg';
        const titleTextencodedName = metaTitle.getAttribute('content') || ''encodeURIComponent(filename);

        const matchcandidates = titleText.match(versionPattern)[];
        if (matchpageId) return match[1];
      }

{
          // thumbnails costuma funcionar melhor no Confluence
       const titleMatch = (document.title || '').match(versionPattern);
candidates.push('/download/thumbnails/' + pageId + '/' + encodedName + '?api=v2');
       if   (titleMatch) return titleMatch[1];

candidates.push('/download/attachments/' + pageId + '/' + encodedName);
       return null;}
    }

    function getPageId() {
      try {// fallback: página “principal” antiga (se existir o anexo lá também)
        const qs = new URLSearchParams(window.location.searchcandidates.push('/download/attachments/649987902/' + encodedName);

        constlet fromQueryidx = qs.get('pageId')0;
        iffunction tryNext(fromQuery) return String(fromQuery);{
      } catch (e) { /*if ignore */ }

 (idx >= candidates.length) return;
     // Confluence costuma expor isso quandoconst AJSurl está disponível= candidates[idx++];
      try {
   const probe = new  if (window.AJS && AJS.params && AJS.params.pageId) return String(AJS.params.pageId);
Image();
          }probe.onload catch= function(e) { /*
 ignore */ }

      return null;
  logoEl.src = }url;

    function setTotvsLogo() {
      const logoEl.style.display = document.getElementById('totvsinline-logoblock');
      if (!logoEl) return;

    };
         const pageIdprobe.onerror = getPageIdfunction(); {
       const  filename = 'totvs logo.jpg' tryNext();
       const encodedName = encodeURIComponent(filename)};

         const candidatesprobe.src = []url;
      if (pageId) {}

        // thumbnails costuma funcionar melhor no Confluence
  tryNext();
      }

      candidates.push('/download/thumbnails/' + pageId + '/' + encodedName + '?api=v2');
        candidates.push('/download/attachments/' + pageId + '/' + encodedName);
 Tenta resolver o logo mesmo antes de identificar versão/documento
      setTotvsLogo();

      }
function renderNotAvailable() {
        // fallback:Mantém páginao “principal”link antiga (se existir o anexo lá também)) para permitir abrir manualmente no GitHub
        container.innerHTML =
      candidates.push('/download/attachments/649987902/' + encodedName);

  '<div style="padding:18px; border-left:4px solid let idx = 0;
#FFAB00; background:#FFFAE6; color:#172b4d;">' +
       function tryNext() {
   '<div style="font-weight:700;">Esse documento ainda não ifestá (idxdisponível >=no candidates.length) return;momento.</div>' +
        const url = candidates[idx++] '</div>';
      }

  const  probe = newfunction ImagegetVersionOverrideFromQuery(); {
        try {
   probe.onload = function() {
    const qs =    logoEl.src = url;
new URLSearchParams(window.location.search);
          const v = logoEl.style.display = 'inline-block'qs.get('version') || qs.get('versao');
        };
  if (v     probe.onerror = function() {&& /^\d+\.\d+\.\d+(?:\.\d+)?$/.test(v)) return v;
        } catch tryNext(e);
 { /* ignore  */   };
        probe.src = urlreturn null;
      }

      tryNext();
    }

    // Tenta resolver o logo mesmo antes de identificar versão/documento
    setTotvsLogo  const version = getPageVersion();

      const function renderNotAvailable() {
container = document.getElementById('widget-container');
      const //docLink esconder link por padrão quando não tem conteúdo útil para abrir= document.getElementById('doc-link');

      const versionResolved = getVersionOverrideFromQuery() || version;

      if (docLink) docLink.style.display = 'none';
!versionResolved) {
        container.innerHTML =
          '<div style="padding:18px; border-left:4px solid #FFAB00#DE350B; background:#FFFAE6#FFEBE6; color:#172b4d;">' +
            '<div style="font-weight:700;">Esse documento ainda não está disponível no momento.< margin-bottom:6px;">Versão não identificada</div>' +
            '<div>Coloque a versão no título da página, ex: <b>Dicionário de Dados 12.1.2503.14</b></div>' +
          '</div>';
        return;
      }

    const  document.getElementById('version-display').textContent = getPageVersion()versionResolved;
    const container = document.getElementById('widgetpage-containertitle').textContent = 'Dicionário de Dados ' + versionResolved;
    const docLink = document.getElementByIdsetLastUpdateText(new Date().toLocaleString('docpt-linkBR'));

      const ifpathInRepo = buildRepoPath(!versionversionResolved);
 {
     const container.innerHTMLrawUrl =
 GITHUB_RAW_BASE + pathInRepo;
     '<div style="padding:18px; border-left:4px solid #DE350B; background:#FFEBE6; color:#172b4d;">' +
    const blobUrl = GITHUB_BLOB_BASE + pathInRepo;

      // '<div style="font-weight:700; margin-bottom:6px;">Versão não identificada</div>' +Link para o GitHub (visual)
      docLink.href = blobUrl;
  '<div>Coloque a versão no títulodocLink.textContent da= página, ex: <b>Dicionário de Dados 12.1.2503.14</b></div>' +'Abrir no GitHub';
      docLink.style.display = '</div>inline-block';

      return;container.innerHTML =
    }

    document.getElementById('version-display').textContent = version;
    document.getElementById('page-title').textContent = 'Dicionário de Dados ' + version;
    document.getElementById('last-update').textContent = new Date().toLocaleString('pt-BR');

    const entry = versionToFile[version];
    if (!entry'<div style="padding:18px; color:#6b778c;">Carregando conteúdo do GitHub...</div>';

      fetch(rawUrl, { cache: 'no-store' })
        .then(function(resp) {
      renderNotAvailable();
      if (!resp.ok) return;
 { ok:  }

    const fileId = entry.idfalse, text: '', lastModified: null };
    const fileType = entry.type;
    const fileByteslm = (typeof entry.bytes === 'number') ? entry.bytes resp.headers ? resp.headers.get('last-modified') : null;

    // Se o arquivo existir mas estiver vazio (0 bytes), tratar como "ainda não disponível"return resp.text().then(function(t) {
    if  (fileType === 'txt' && fileBytes === 0)return {
 ok: true, text: t, lastModified: lm renderNotAvailable()};
      return;
    });

    // Link SEMPRE para o Drive (como solicitado})
    // (open?id=...) funciona para Google Docs e também para arquivos (.txt/.pdf/etc)
    const driveOpenUrl = 'https://drive.google.com/open?id=' + fileId    .then(function(res) {
          const txt = ((res && res.text) ? res.text : '').trim();
    docLink.href = driveOpenUrl;
    docLink.style.display = 'inline-block';

if (!txt) {
      // Preview: Docs preview para Google DocrenderNotAvailable();
  Drive preview para txt
    const previewUrl = (fileType === 'doc')
return;
        ? ('https://docs.google.com/document/d/' + fileId + '/preview')
 }

         : ('https://drive.google.com/file/d/' + fileId + '/preview');

    // Modo de edição: mostrar macro Widget Connector (URL do Drive)
    const isEditMode = window.location.pathname.includes('/pages/editpage.action');
    if (isEditMode) {// 1) Fallback rápido: Last-Modified do RAW (não é exatamente "commit date", mas aproxima)
          const lm = res && res.lastModified ? formatDatePtBr(res.lastModified) : null;
          if (lm) setLastUpdateText(lm);

      const widgetCode =
    // 2) Data  '<ac:structured-macro ac:name="widget" ac:schema-version="1">\n' +
        '  <ac:parameter ac:name="url">' + driveOpenUrl + '</ac:parameter>\n' +real do commit via GitHub API (se permitido por CSP)
          tryFetchCommitDate(pathInRepo).then(function(commitIso) {
        '  <ac:parameter ac:name="width">100%</ac:parameter>\n' +
        '  <ac:parameter ac:name="height">800</ac:parameter>\n' +
        '</ac:structured-macro>';

 const formatted = commitIso ? formatDatePtBr(commitIso) : null;
          const escaped =if widgetCode.replace(/</g, '<').replace(/>/g, '>'(formatted) setLastUpdateText(formatted);
      container.innerHTML =
   });

     '<div style="padding:16px;">' +
   // Renderiza texto em <pre>   '<div style="font-weight:700; margin-bottom:8px; color:#172b4d;">Código do Widget Connector</div>' +preservando formatação
          const pre = document.createElement('pre');
          '<pre style="white-space:pre.style.whiteSpace = 'pre-wrap';
  background:#f4f5f7; border:1px solid #dfe1e6; padding:12px; border-radius:6px;">' + escaped + '</pre>' +    pre.style.margin = '0';
          '<div style="color:#6b778c; margin-top:8px;">Copie o código acima e cole no macro <b>Widget Connector</b>.</div>' +
pre.style.padding = '16px';
          pre.style.borderTop = '1px solid #dfe1e6';
          pre.style.fontFamily = '</div>';
      returnConsolas, Menlo, Monaco, \"Courier New\", monospace';
    }

     const iframepre.textContent = document.createElement('iframe')txt;

    iframe.src = previewUrl;
    iframecontainer.widthinnerHTML = '100%';
     iframe.height = '800';
     container.appendChild(pre);
      iframe.style.border = '0'; })
    iframe.setAttribute('allowfullscreen', 'true');    .catch(function() {
    iframe.title = 'Dicionário de Dados ' + version renderNotAvailable();

    container.innerHTML = '';
    container.appendChild(iframe});
    })(); 
    </script>
  </body>
  </html>