├── Code.gs ├── ContainerSelector.html ├── ContainerSelectorJavaScript.html ├── MarkChangesJavaScript.html ├── MarkChangesModal.html ├── PushChangesJavaScript.html ├── PushChangesModal.html ├── README.md └── Styles.html /Code.gs: -------------------------------------------------------------------------------- 1 | /* GTM API methods */ 2 | 3 | function fetchLatestVersion(aid, cid, vid) { 4 | var parent = 'accounts/' + aid + '/containers/' + cid + '/versions/' + vid; 5 | return TagManager.Accounts.Containers.Versions.get(parent); 6 | } 7 | 8 | function fetchLatestVersionId(aid, cid) { 9 | var parent = 'accounts/' + aid + '/containers/' + cid; 10 | return TagManager.Accounts.Containers.Version_headers.latest(parent, { 11 | fields: 'containerVersionId' 12 | }).containerVersionId; 13 | } 14 | 15 | function fetchAccounts() { 16 | var accounts = TagManager.Accounts.list({ 17 | fields: 'account(accountId,name)' 18 | }).account; 19 | return accounts || []; 20 | } 21 | 22 | function fetchContainers(aid) { 23 | var parent = 'accounts/' + aid; 24 | var containers = TagManager.Accounts.Containers.list(parent, { 25 | fields: 'container(accountId,containerId,publicId,name)' 26 | }).container; 27 | return containers || []; 28 | } 29 | 30 | function createVersion(aid, cid, wsid) { 31 | return TagManager.Accounts.Containers.Workspaces.create_version({"name": "Created by GTM Tools Google Sheets add-on", "notes": "Created by GTM Tools Google Sheets add-on"}, 'accounts/' + aid + '/containers/' + cid + '/workspaces/' + wsid).containerVersion; 32 | } 33 | 34 | function getWorkspaces() { 35 | var apiPath = getApiPath(); 36 | 37 | if (!apiPath) { 38 | return false; 39 | } 40 | 41 | return TagManager.Accounts.Containers.Workspaces.list(apiPath, { 42 | fields: 'workspace(name, workspaceId)' 43 | }).workspace; 44 | } 45 | 46 | function fetchContainersWithSelectedMarked(aid) { 47 | var containerSummary = fetchContainers(aid); 48 | var selectedContainerId = getContainerIdFromApiPath(); 49 | containerSummary.forEach(function(cont) { 50 | cont.selected = cont.containerId === selectedContainerId; 51 | }); 52 | return containerSummary; 53 | } 54 | 55 | function fetchAccountsWithSelectedMarked() { 56 | var accountSummary = fetchAccounts(); 57 | var selectedAccountId = getAccountIdFromApiPath(); 58 | accountSummary.forEach(function(acct) { 59 | acct.selected = acct.accountId === selectedAccountId; 60 | }); 61 | return accountSummary; 62 | } 63 | 64 | function getContainerPublicIdFromSheetName() { 65 | var sheet = SpreadsheetApp.getActiveSheet().getName(); 66 | var cid = sheet.match(/^GTM-[a-zA-Z0-9]{4,}/) || []; 67 | return cid.length ? cid[0] : 'N/A'; 68 | } 69 | 70 | function getAccountIdFromApiPath() { 71 | var apiPath = getApiPath(); 72 | return apiPath ? apiPath.split('/')[1] : ''; 73 | } 74 | 75 | function getContainerIdFromApiPath() { 76 | var apiPath = getApiPath(); 77 | return apiPath ? apiPath.split('/')[3] : ''; 78 | } 79 | 80 | function getApiPath() { 81 | var sheet = SpreadsheetApp.getActiveSheet().getName(); 82 | if (!/^GTM-[a-zA-Z0-9]{4,}_(container|tags|variables|triggers)$/.test(sheet)) { 83 | return false; 84 | } 85 | var containerSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheet.replace(/_.+$/,'_container')); 86 | var apiPath = containerSheet.getRange('B10').getValue().replace(/\/versions\/.*/, ''); 87 | return apiPath; 88 | } 89 | 90 | function insertSheet(sheetName) { 91 | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName); 92 | var ui = SpreadsheetApp.getUi(); 93 | var response; 94 | if (sheet) { 95 | response = ui.alert('Sheet named ' + sheetName + ' already exists! Click OK to overwrite, CANCEL to skip.', ui.ButtonSet.OK_CANCEL); 96 | return response === ui.Button.OK ? sheet : false; 97 | } 98 | return SpreadsheetApp.getActiveSpreadsheet().insertSheet(sheetName); 99 | } 100 | 101 | function getAssetOverview(assets) { 102 | var assetlist = {}; 103 | var sortedlist = []; 104 | var sum = 0; 105 | assets.forEach(function(item) { 106 | if (!assetlist[item.type]) { 107 | assetlist[item.type] = 1; 108 | } else { 109 | assetlist[item.type] += 1; 110 | } 111 | sum += 1; 112 | }); 113 | for (var item in assetlist) { 114 | sortedlist.push([item, assetlist[item]]); 115 | } 116 | sortedlist = sortedlist.sort(function(a,b) { 117 | return b[1] - a[1]; 118 | }); 119 | return { 120 | sortedlist: sortedlist.length === 0 ? [['','']] : sortedlist, 121 | sum: sum 122 | } 123 | } 124 | 125 | function buildRangesObject() { 126 | var namedRanges = SpreadsheetApp.getActiveSpreadsheet().getNamedRanges(); 127 | var rangesObject = {}; 128 | 129 | namedRanges.forEach(function(range) { 130 | var name = range.getName(); 131 | if (/(_notes|_json)$/.test(name)) { 132 | var bareName = name.replace(/(_notes|_json)$/g, ''); 133 | rangesObject[bareName] = rangesObject[bareName] || {}; 134 | if (/_notes$/.test(name)) { 135 | rangesObject[bareName].notes = range.getRange(); 136 | } 137 | if (/_json$/.test(name)) { 138 | rangesObject[bareName].json = range.getRange(); 139 | } 140 | rangesObject[bareName].accountId = name.split('_')[1]; 141 | rangesObject[bareName].containerId = name.split('_')[2]; 142 | } 143 | }); 144 | 145 | return rangesObject; 146 | } 147 | 148 | function updateSingleNote(noteToUpdate, wsid) { 149 | var json = noteToUpdate.json; 150 | json.notes = noteToUpdate.note; 151 | 152 | var path = 'accounts/' + noteToUpdate.json.accountId + '/containers/' + noteToUpdate.json.containerId + '/workspaces/' + wsid; 153 | 154 | if ('tagId' in json) { 155 | return TagManager.Accounts.Containers.Workspaces.Tags.update(JSON.stringify(json), path + '/tags/' + json.tagId); 156 | } 157 | if ('triggerId' in json) { 158 | return TagManager.Accounts.Containers.Workspaces.Triggers.update(JSON.stringify(json), path + '/triggers/' + json.triggerId); 159 | } 160 | if ('variableId' in json) { 161 | return TagManager.Accounts.Containers.Workspaces.Variables.update(JSON.stringify(json), path + '/variables/' + json.variableId); 162 | } 163 | } 164 | 165 | function markChangedNotes() { 166 | var rangesObject = buildRangesObject(); 167 | var count = 0; 168 | 169 | if (Object.keys(rangesObject).length === 0) { 170 | throw new Error('No valid documentation sheets found. Remember to run the Build documentation menu option first!'); 171 | } 172 | 173 | for (var item in rangesObject) { 174 | var notes = rangesObject[item].notes.getValues(); 175 | var json = rangesObject[item].json.getValues(); 176 | notes.forEach(function(note, index) { 177 | var cell = rangesObject[item].notes.getCell(index + 1, 1); 178 | var jsonNote = JSON.parse(json[index]).notes || ''; 179 | if (note[0] === jsonNote) { 180 | cell.setBackground('#fff'); 181 | } else if (note[0] !== jsonNote) { 182 | cell.setBackground('#f6b26b'); 183 | count++; 184 | } 185 | }); 186 | } 187 | 188 | return count; 189 | } 190 | 191 | function processNotes(action) { 192 | var rangesObject = buildRangesObject(); 193 | var notesToUpdate = []; 194 | var selectedAccountId = getAccountIdFromApiPath(); 195 | var selectedContainerId = getContainerIdFromApiPath(); 196 | 197 | for (var item in rangesObject) { 198 | var notes = rangesObject[item].notes.getValues(); 199 | var json = rangesObject[item].json.getValues(); 200 | notes.forEach(function(note, index) { 201 | var cell = rangesObject[item].notes.getCell(index + 1, 1); 202 | var jsonNote = JSON.parse(json[index]).notes || ''; 203 | if (note[0] === jsonNote) { 204 | cell.setBackground('#fff'); 205 | } else if (note[0] !== jsonNote) { 206 | if (action === 'mark') { 207 | cell.setBackground('#f6b26b'); 208 | } 209 | if (action === 'push' && selectedAccountId === rangesObject[item].accountId && selectedContainerId === rangesObject[item].containerId) { 210 | cell.setBackground('#fff'); 211 | notesToUpdate.push({ 212 | note: note[0], 213 | json: JSON.parse(json[index]) 214 | }); 215 | } 216 | } 217 | }); 218 | } 219 | 220 | return notesToUpdate; 221 | } 222 | 223 | function formatTags(tags) { 224 | var data = []; 225 | tags.forEach(function(tag) { 226 | data.push([ 227 | tag.name, 228 | tag.tagId, 229 | tag.type, 230 | tag.parentFolderId || '', 231 | new Date(parseInt(tag.fingerprint)), 232 | tag.firingTriggerId ? tag.firingTriggerId.join(',') : '', 233 | tag.blockingTriggerId ? tag.blockingTriggerId.join(',') : '', 234 | tag.setupTag ? tag.setupTag[0].tagName : '', 235 | tag.teardownTag ? tag.teardownTag[0].tagName : '', 236 | tag.notes || '', 237 | JSON.stringify(tag) 238 | ]); 239 | }); 240 | return data; 241 | } 242 | 243 | function formatVariables(variables) { 244 | var data = []; 245 | variables.forEach(function(variable) { 246 | data.push([ 247 | variable.name, 248 | variable.variableId, 249 | variable.type, 250 | variable.parentFolderId || '', 251 | new Date(parseInt(variable.fingerprint)), 252 | variable.notes || '', 253 | JSON.stringify(variable) 254 | ]); 255 | }); 256 | return data; 257 | } 258 | 259 | function formatTriggers(triggers) { 260 | var data = []; 261 | triggers.forEach(function(trigger) { 262 | data.push([ 263 | trigger.name, 264 | trigger.triggerId, 265 | trigger.type, 266 | trigger.parentFolderId || '', 267 | new Date(parseInt(trigger.fingerprint)), 268 | trigger.notes || '', 269 | JSON.stringify(trigger) 270 | ]); 271 | }); 272 | return data; 273 | } 274 | 275 | function clearInvalidRanges() { 276 | var storedRanges = JSON.parse(PropertiesService.getUserProperties().getProperty('named_ranges')) || {}; 277 | var storedRangesNames = Object.keys(storedRanges); 278 | 279 | var namedRanges = SpreadsheetApp.getActiveSpreadsheet().getNamedRanges(); 280 | var namedRangesNames = namedRanges.map(function(a) { return a.getName(); }); 281 | 282 | storedRangesNames.forEach(function(storedRangeName) { 283 | if (namedRangesNames.indexOf(storedRangeName) === -1) { 284 | SpreadsheetApp.getActiveSpreadsheet().removeNamedRange(storedRangeName); 285 | delete storedRanges[storedRangeName]; 286 | } 287 | }); 288 | 289 | PropertiesService.getUserProperties().setProperty('named_ranges', JSON.stringify(storedRanges)); 290 | } 291 | 292 | function setNamedRanges(sheet,rangeName,notesIndex,jsonIndex,colLength) { 293 | var notesRange = sheet.getRange(3,notesIndex,colLength,1); 294 | var notesRangeName = rangeName + '_notes'; 295 | SpreadsheetApp.getActiveSpreadsheet().setNamedRange(notesRangeName, SpreadsheetApp.getActiveSpreadsheet().getRange(sheet.getName() + '!' + notesRange.getA1Notation())); 296 | var jsonRange = sheet.getRange(3,jsonIndex,colLength,1); 297 | var jsonRangeName = rangeName + '_json'; 298 | SpreadsheetApp.getActiveSpreadsheet().setNamedRange(jsonRangeName, SpreadsheetApp.getActiveSpreadsheet().getRange(sheet.getName() + '!' + jsonRange.getA1Notation())); 299 | 300 | var ranges = JSON.parse(PropertiesService.getUserProperties().getProperty('named_ranges')) || {}; 301 | ranges[notesRangeName] = true; 302 | ranges[jsonRangeName] = true; 303 | PropertiesService.getUserProperties().setProperty('named_ranges', JSON.stringify(ranges)); 304 | } 305 | 306 | function createHeaders(sheet, labels, title) { 307 | var headerRange = sheet.getRange(1,1,1,labels.length); 308 | headerRange.mergeAcross(); 309 | headerRange.setValue(title); 310 | headerRange.setBackground('#1155cc'); 311 | headerRange.setFontWeight('bold'); 312 | headerRange.setFontColor('white'); 313 | 314 | var labelsRange = sheet.getRange(2,1,1,labels.length); 315 | labelsRange.setValues([labels]); 316 | labelsRange.setFontWeight('bold'); 317 | } 318 | 319 | function buildTriggerSheet(containerObj) { 320 | var sheetName = containerObj.containerPublicId + '_triggers'; 321 | var sheet = insertSheet(sheetName); 322 | 323 | if (sheet === false) { return; } 324 | 325 | sheet.clear(); 326 | 327 | var triggerLabels = ['Trigger name', 'Trigger ID', 'Trigger type', 'Folder ID', 'Last modified', 'Notes', 'JSON (do NOT edit!)']; 328 | 329 | createHeaders(sheet, triggerLabels, 'Triggers for container ' + containerObj.containerPublicId + ' (' + containerObj.containerName + ').'); 330 | 331 | sheet.setColumnWidth(1, 305); 332 | sheet.setColumnWidth(2, 75); 333 | sheet.setColumnWidth(3, 100); 334 | sheet.setColumnWidth(4, 75); 335 | sheet.setColumnWidth(5, 130); 336 | sheet.setColumnWidth(6, 305); 337 | sheet.setColumnWidth(7, 130); 338 | 339 | var triggersObject = formatTriggers(containerObj.triggers); 340 | if (triggersObject.length) { 341 | var dataRange = sheet.getRange(3,1,triggersObject.length,triggerLabels.length); 342 | dataRange.setValues(triggersObject); 343 | dataRange.setBackground('#fff'); 344 | 345 | var rangeName = 'triggers_' + containerObj.accountId + '_' + containerObj.containerId; 346 | setNamedRanges(sheet,rangeName,triggerLabels.indexOf('Notes') + 1,triggerLabels.indexOf('JSON (do NOT edit!)') + 1,triggersObject.length); 347 | 348 | var formats = triggersObject.map(function(a) { 349 | return ['@', '@', '@', '@', 'dd/mm/yy at h:mm', '@', '@']; 350 | }); 351 | dataRange.setNumberFormats(formats); 352 | dataRange.setHorizontalAlignment('left'); 353 | } 354 | } 355 | 356 | function buildVariableSheet(containerObj) { 357 | var sheetName = containerObj.containerPublicId + '_variables'; 358 | var sheet = insertSheet(sheetName); 359 | 360 | if (sheet === false) { return; } 361 | 362 | sheet.clear(); 363 | 364 | var variableLabels = ['Variable name', 'Variable ID', 'Variable type', 'Folder ID', 'Last modified', 'Notes', 'JSON (do NOT edit!)']; 365 | 366 | createHeaders(sheet, variableLabels, 'Variables for container ' + containerObj.containerPublicId + ' (' + containerObj.containerName + ').'); 367 | 368 | sheet.setColumnWidth(1, 305); 369 | sheet.setColumnWidth(2, 75); 370 | sheet.setColumnWidth(3, 100); 371 | sheet.setColumnWidth(4, 75); 372 | sheet.setColumnWidth(5, 130); 373 | sheet.setColumnWidth(6, 305); 374 | sheet.setColumnWidth(7, 130); 375 | 376 | var variablesObject = formatVariables(containerObj.variables); 377 | if (variablesObject.length) { 378 | var dataRange = sheet.getRange(3,1,variablesObject.length,variableLabels.length); 379 | dataRange.setValues(variablesObject); 380 | dataRange.setBackground('#fff'); 381 | 382 | var rangeName = 'variables_' + containerObj.accountId + '_' + containerObj.containerId; 383 | setNamedRanges(sheet,rangeName,variableLabels.indexOf('Notes') + 1,variableLabels.indexOf('JSON (do NOT edit!)') + 1,variablesObject.length); 384 | 385 | var formats = variablesObject.map(function(a) { 386 | return ['@', '@', '@', '@', 'dd/mm/yy at h:mm', '@', '@']; 387 | }); 388 | dataRange.setNumberFormats(formats); 389 | dataRange.setHorizontalAlignment('left'); 390 | } 391 | } 392 | 393 | function buildTagSheet(containerObj) { 394 | var sheetName = containerObj.containerPublicId + '_tags'; 395 | var sheet = insertSheet(sheetName); 396 | 397 | if (sheet === false) { return; } 398 | 399 | sheet.clear(); 400 | 401 | var tagLabels = ['Tag name', 'Tag ID', 'Tag type', 'Folder ID', 'Last modified', 'Firing trigger IDs', 'Exception trigger IDs', 'Setup tag', 'Cleanup tag', 'Notes', 'JSON (do NOT edit!)']; 402 | 403 | createHeaders(sheet, tagLabels, 'Tags for container ' + containerObj.containerPublicId + ' (' + containerObj.containerName + ').'); 404 | 405 | sheet.setColumnWidth(1, 305); 406 | sheet.setColumnWidth(2, 75); 407 | sheet.setColumnWidth(3, 100); 408 | sheet.setColumnWidth(4, 75); 409 | sheet.setColumnWidth(5, 130); 410 | sheet.setColumnWidth(6, 150); 411 | sheet.setColumnWidth(7, 150); 412 | sheet.setColumnWidth(8, 205); 413 | sheet.setColumnWidth(9, 205); 414 | sheet.setColumnWidth(10, 305); 415 | sheet.setColumnWidth(11, 130); 416 | 417 | var tagsObject = formatTags(containerObj.tags); 418 | if (tagsObject.length) { 419 | var dataRange = sheet.getRange(3,1,tagsObject.length,tagLabels.length); 420 | dataRange.setValues(tagsObject); 421 | dataRange.setBackground('#fff'); 422 | 423 | var rangeName = 'tags_' + containerObj.accountId + '_' + containerObj.containerId; 424 | setNamedRanges(sheet,rangeName,tagLabels.indexOf('Notes') + 1,tagLabels.indexOf('JSON (do NOT edit!)') + 1,tagsObject.length); 425 | 426 | var formats = tagsObject.map(function(a) { 427 | return ['@', '@', '@', '@', 'dd/mm/yy at h:mm', '@', '@', '@', '@', '@', '@']; 428 | }); 429 | dataRange.setNumberFormats(formats); 430 | dataRange.setHorizontalAlignment('left'); 431 | } 432 | } 433 | 434 | function buildContainerSheet(containerObj) { 435 | var sheetName = containerObj.containerPublicId + '_container'; 436 | var sheet = insertSheet(sheetName); 437 | 438 | if (sheet === false) { return; } 439 | 440 | sheet.setColumnWidth(1, 190); 441 | sheet.setColumnWidth(2, 340); 442 | 443 | var containerHeader = sheet.getRange(1,1,1,2); 444 | containerHeader.setValues([['Google Tag Manager documentation','']]); 445 | containerHeader.mergeAcross(); 446 | containerHeader.setBackground('#1155cc'); 447 | containerHeader.setFontWeight('bold'); 448 | containerHeader.setHorizontalAlignment('center'); 449 | containerHeader.setFontColor('white'); 450 | 451 | var containerLabels = ['Container ID:', 'Container name:', 'Container notes:', 'Latest version ID:', 'Version name:', 'Version description:', 'Version created/published:', 'Link to container:', 'API path:']; 452 | 453 | var containerContent = sheet.getRange(2,1,containerLabels.length,2); 454 | containerContent.setValues([ 455 | [containerLabels[0], containerObj.containerPublicId], 456 | [containerLabels[1], containerObj.containerName], 457 | [containerLabels[2], containerObj.containerNotes], 458 | [containerLabels[3], containerObj.versionId], 459 | [containerLabels[4], containerObj.versionName], 460 | [containerLabels[5], containerObj.versionDescription], 461 | [containerLabels[6], containerObj.versionCreatedOrPublished], 462 | [containerLabels[7], containerObj.containerLink], 463 | [containerLabels[8], 'accounts/' + containerObj.accountId + '/containers/' + containerObj.containerId + '/versions/' + containerObj.versionId] 464 | ]); 465 | containerContent.setBackgrounds([ 466 | ['white', 'white'], 467 | ['#e8ebf8', '#e8ebf8'], 468 | ['white', 'white'], 469 | ['#e8ebf8', '#e8ebf8'], 470 | ['white', 'white'], 471 | ['#e8ebf8', '#e8ebf8'], 472 | ['white', 'white'], 473 | ['#e8ebf8', '#e8ebf8'], 474 | ['white', 'white'] 475 | ]); 476 | containerContent.setNumberFormats([ 477 | ['@', '@'], 478 | ['@', '@'], 479 | ['@', '@'], 480 | ['@', '@'], 481 | ['@', '@'], 482 | ['@', '@'], 483 | ['@', 'dd/mm/yy at h:mm'], 484 | ['@', '@'], 485 | ['@', '@'] 486 | ]); 487 | containerContent.setVerticalAlignment('top'); 488 | 489 | var containerLabelCol = sheet.getRange(2,1,containerLabels.length,1); 490 | containerLabelCol.setFontWeight('bold'); 491 | containerLabelCol.setHorizontalAlignment('right'); 492 | 493 | var containerDataCol = sheet.getRange(2,2,containerLabels.length,1); 494 | containerDataCol.setHorizontalAlignment('left'); 495 | 496 | var emptyCellFix = sheet.getRange(2,3,containerLabels.length,1); 497 | var emptyCells = []; 498 | for (var i = 0; i < containerLabels.length; i++) { 499 | emptyCells.push([' ']); 500 | } 501 | emptyCellFix.setValues(emptyCells); 502 | 503 | var overviewHeader = sheet.getRange(1,4,1,8); 504 | overviewHeader.setValues([['Overview of contents', '', '', '', '', '', '', '']]); 505 | overviewHeader.mergeAcross(); 506 | overviewHeader.setBackground('#85200c'); 507 | overviewHeader.setFontWeight('bold'); 508 | overviewHeader.setHorizontalAlignment('center'); 509 | overviewHeader.setFontColor('white'); 510 | 511 | var overviewSubHeader = sheet.getRange(2,4,1,8); 512 | overviewSubHeader.setValues([['Tag type', 'Quantity', 'Trigger type', 'Quantity', 'Variable type', 'Quantity', 'Folder ID', 'Folder name']]); 513 | overviewSubHeader.setHorizontalAlignments([['right','left','right','left','right','left', 'right', 'left']]); 514 | overviewSubHeader.setFontWeight('bold'); 515 | overviewSubHeader.setBackground('#e6d6d6'); 516 | 517 | var tags = getAssetOverview(containerObj.tags); 518 | var tagsRange = sheet.getRange(3,4,tags.sortedlist.length,2); 519 | var tagsSum = tags.sum; 520 | tagsRange.setValues(tags.sortedlist); 521 | sheet.getRange(3,4,tags.sortedlist.length,1).setHorizontalAlignment('right'); 522 | sheet.getRange(3,5,tags.sortedlist.length,1).setHorizontalAlignment('left'); 523 | 524 | var triggers = getAssetOverview(containerObj.triggers); 525 | var triggersRange = sheet.getRange(3,6,triggers.sortedlist.length,2); 526 | var triggersSum = triggers.sum; 527 | triggersRange.setValues(triggers.sortedlist); 528 | sheet.getRange(3,6,triggers.sortedlist.length,1).setHorizontalAlignment('right'); 529 | sheet.getRange(3,7,triggers.sortedlist.length,1).setHorizontalAlignment('left'); 530 | 531 | var variables = getAssetOverview(containerObj.variables); 532 | var variablesRange = sheet.getRange(3,8,variables.sortedlist.length,2); 533 | var variablesSum = variables.sum; 534 | variablesRange.setValues(variables.sortedlist); 535 | sheet.getRange(3,8,variables.sortedlist.length,1).setHorizontalAlignment('right'); 536 | sheet.getRange(3,9,variables.sortedlist.length,1).setHorizontalAlignment('left'); 537 | 538 | var folders = containerObj.folders.map(function(folder) { 539 | return [folder.folderId, folder.name]; 540 | }); 541 | if (folders.length) { 542 | var foldersRange = sheet.getRange(3,10,folders.length,2); 543 | foldersRange.setValues(folders); 544 | } 545 | 546 | var contentLength = Math.max(tags.sortedlist.length, variables.sortedlist.length, triggers.sortedlist.length, folders.length); 547 | var totalRow = sheet.getRange(contentLength + 3, 4, 1, 8); 548 | totalRow.setValues([ 549 | ['Total tags:', tagsSum, 'Total triggers:', triggersSum, 'Total variables:', variablesSum, '', ''] 550 | ]); 551 | totalRow.setHorizontalAlignments([['right', 'left', 'right', 'left', 'right', 'left', 'right', 'left']]); 552 | totalRow.setFontWeight('bold'); 553 | totalRow.setBackground('#e6d6d6'); 554 | } 555 | 556 | function startProcess(aid, cid) { 557 | var latestVersionId = fetchLatestVersionId(aid, cid); 558 | if (latestVersionId === '0') { throw new Error('You need to create or publish a version in the container before you can build its documentaiton!'); } 559 | var latestVersion = fetchLatestVersion(aid, cid, latestVersionId); 560 | var containerObj = { 561 | accountId: latestVersion.container.accountId, 562 | containerId: latestVersion.container.containerId, 563 | containerName: latestVersion.container.name, 564 | containerPublicId: latestVersion.container.publicId, 565 | containerNotes: latestVersion.container.notes || '', 566 | containerLink: latestVersion.container.tagManagerUrl, 567 | versionName: latestVersion.name || '', 568 | versionId: latestVersion.containerVersionId, 569 | versionDescription: latestVersion.description || '', 570 | versionCreatedOrPublished: new Date(parseInt(latestVersion.fingerprint)), 571 | tags: latestVersion.tag || [], 572 | variables: latestVersion.variable || [], 573 | triggers: latestVersion.trigger || [], 574 | folders: latestVersion.folder || [] 575 | }; 576 | buildContainerSheet(containerObj); 577 | buildTagSheet(containerObj); 578 | buildTriggerSheet(containerObj); 579 | buildVariableSheet(containerObj); 580 | } 581 | 582 | function include(filename) { 583 | return HtmlService.createHtmlOutputFromFile(filename) 584 | .getContent(); 585 | } 586 | 587 | function openContainerSelector() { 588 | var ui = SpreadsheetApp.getUi(); 589 | var html = HtmlService.createTemplateFromFile('ContainerSelector').evaluate().setWidth(400).setHeight(220); 590 | SpreadsheetApp.getUi().showModalDialog(html, 'Build documentation'); 591 | } 592 | 593 | function openMarkChangesModal() { 594 | clearInvalidRanges(); 595 | var ui = SpreadsheetApp.getUi(); 596 | if (Object.keys(buildRangesObject()).length === 0) { 597 | ui.alert('No valid documentation sheets found! Run "Build documentation" if necessary.'); 598 | return; 599 | } 600 | var html = HtmlService.createTemplateFromFile('MarkChangesModal').evaluate().setWidth(400).setHeight(100); 601 | SpreadsheetApp.getUi().showModalDialog(html, 'Mark changes to Notes'); 602 | } 603 | 604 | function openPushChangesModal() { 605 | clearInvalidRanges(); 606 | var ui = SpreadsheetApp.getUi(); 607 | if (getApiPath() === false) { 608 | ui.alert('You need to have a valid documentation sheet selected first! Run "Build documentation" if necessary.', ui.ButtonSet.OK); 609 | return; 610 | } 611 | var html = HtmlService.createTemplateFromFile('PushChangesModal').evaluate().setWidth(400).setHeight(280); 612 | SpreadsheetApp.getUi().showModalDialog(html, 'Push changes to Notes'); 613 | } 614 | 615 | function onOpen() { 616 | var menu = SpreadsheetApp.getUi().createAddonMenu(); 617 | menu.addItem('Build documentation', 'openContainerSelector') 618 | menu.addItem('Mark changes to Notes', 'openMarkChangesModal') 619 | menu.addItem('Push changes to Notes', 'openPushChangesModal') 620 | menu.addToUi(); 621 | } 622 | -------------------------------------------------------------------------------- /ContainerSelector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |