Is there any way to add a template doc at top of an another google document using google scripting? - google-cloud-platform

I am trying to add a template doc into an existing google doc.The template is being added, but next time when i am trying to add the template again the template is appending at the bottom of the existing google doc but i want to insert the template at the top.
You can do this by getting the Body of one document and appending its child Elements to the current document.
function addtemplate() {
var thisDoc = DocumentApp.getActiveDocument();
var thisBody = thisDoc.getBody();
var templateDoc = DocumentApp.openById(''); //Pass in id of doc to be used as a template.
var templateBody = templateDoc.getBody();
for(var i=0; i<templateBody.getNumChildren();i++){ //run through the elements of the template doc's Body.
switch (templateBody.getChild(i).getType()) { //Deal with the various types of Elements we will encounter and append.
case DocumentApp.ElementType.PARAGRAPH:
thisBody.appendParagraph(templateBody.getChild(i).copy());
break;
case DocumentApp.ElementType.LIST_ITEM:
thisBody.appendListItem(templateBody.getChild(i).copy());
break;
case DocumentApp.ElementType.TABLE:
thisBody.appendTable(templateBody.getChild(i).copy());
break;
}
}
return thisDoc;
}

It sounds like your goal is to choose the position of the document where the content is added?
One option is to add the template at your current cursor location.
In the example below I have two functions. The first function creates a menu in Google Docs when I open the document (usually a few seconds delay).
The second function attempts to identify the position of my cursor. If it's successful it will insert the date at my cursor position.
Since I've created a menu item I don't have to go to the script editor to trigger this function.
function onOpen() {
// Add a menu item in Google Docs.
DocumentApp.getUi().createMenu('Insert Menu')
.addItem('Insert Current Date', 'insertCurrentDate')
.addToUi();
}
function insertCurrentDate() {
var cursor = DocumentApp.getActiveDocument().getCursor();
if (cursor) {
// Attempt to insert text at the cursor position. If insertion returns null,
// then the cursor's containing element doesn't allow text insertions.
var date = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd");
var element = cursor.insertText(date);
if (element) {
element.setBold(true);
} else {
DocumentApp.getUi().alert('Document does not allow inserted text at this location.');
}
} else {
DocumentApp.getUi().alert('Cannot find a cursor in the document.');
}
}
It's also possible that you want to clear the previous template before pasting the new one in? You can do that with the clear() function and then run the rest of your code.
body.clear()

Related

Delete All records in a Siebel List View

I need to Delete All the records in a list applet by a button clicked method. In order to achieve this, I have added a button to the list applet with "DeleteAllRecords" method and added a script to BC PreInvokeMethod function as below. Once clicked on the button all the records will get deleted, but the applet won't get refreshed. How Can I achieve that?
try
{
if(MethodName == "DeleteAllRecords")
{
var EmpBO = TheApplication().GetBusObject("XXXX");
var EmpBC = EmpBO.GetBusComp("XXXX");
with(EmpBC)
{
SetViewMode(AllView);
ActivateField("EmployeeID");
ClearToQuery();
SetSearchExpr("[XXXX] <> ' '");
ExecuteQuery(ForwardOnly);
var Frecord = FirstRecord();
while(Frecord)
{
Frecord = DeleteRecord();
Frecord = FirstRecord();
}
}
//BC.InvokeMethod("RefreshBuscomp");
return (CancelOperation);
}
}
catch(e)
{
throw(e);
}
finally
{
EmpBO = null;
EmpBC = null;
}
return (ContinueOperation);
You could try a few things.
1: A simple null query in the end after all deletions are done. ClearToQuery() and ExecuteQuery();
2: There is a BC method for RefreshBusComp (check documentation) which can be used with InvokeMethod(). Does pretty much the same thing as 1. I see you already tried it and probably did not work.
3: There are Business services which can refresh applets , like FINS Teller UI navigation.
4: You could also try a null query in browserscript/javascript, put that in InvokeMethod, so that it runs after the BC escript. Genb has to be run to generate bscripts.
One of these should work .

Simple relative cell referencing in google code

I have made this virtual inventory with buttons that copy and paste values to various sheets for different types of reports. The code might not be optimal I am not much of a programmer but I'm trying my best. now I have the same button in multiple places and I want it to copy values in cells relative to the position of the button itself but I am not sure how to reference a cell in a relative manner in the getRange fucntion.
here is the code:
function Go() {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var sheet = SpreadsheetApp.getActiveSheet();
var destSheet = ss.getSheetByName("Fiche");
var lastRow = destSheet.getLastRow();
var source = ss.getRange ('(rowid-3)(colid)');
var checkbox = sheet.getRange (3,3);
var destColumn = ss.getRange ('B10').getValues ();
source.copyTo(destSheet.getRange(lastRow + 1,1), {contentsOnly: true});
if (checkbox.getValue() == 'vrai' ) {
var source = ss.getRange ('B9');
source.copyTo(destSheet.getRange(lastRow + 1, destColumn), {contentsOnly: true});
source.copyTo(destSheet.getRange(lastRow + 1, 2), {contentsOnly: true});
}
else {
var nombre = ss.getRange ('D5').getValues() ;
destSheet.getRange(lastRow + 1, destColumn).setValue(nombre);
destSheet.getRange(lastRow + 1, 2).setValue(nombre)
}
I want all (B10),(B9), etc. format cells to be replaced with relative cell positons. I have tried with (colID)(rowID) but it doesn't seem to work
I believe your goal as follows.
You want to retrieve the cell coordinate when a button on Spreadsheet is clicked.
Modification points:
Unfortunately, in the current stage, when a button on Spreadsheet is clicked, there are no methods for directly retrieving the information where button is clicked.
So when you want to use the button on Spreadsheet, if you want to retrieve the information, it is required to prepare each script for each button. But I thought that this might be different from the direction you expect.
In this answer, in order to retrieve the information where the button is clicked, I would like to propose the following 2 patterns as the workarounds. In these patterns, the event object is used. By this, the information of the button can be retrieved. This is used.
Pattern 1:
In this pattern, a cell is used as the button using the simple trigger of OnSelectionChange. When the cell "B3" is used as the button like above image, the script is as follows.
Sample script:
In this script, for example, when you want to use the cell "B3" of "Sheet1" as the button, please set "B3" to buttonRanges. And, set sheet.getSheetName() != "Sheet1". When when you want to use the cell "B3" and "B4" as the button, please set "B3" and "B4" to buttonRanges.
function onSelectionChange(e) {
const range = e.range;
const sheet = range.getSheet();
const buttonRanges = ["B3"];
if (sheet.getSheetName() != "Sheet1" || !buttonRanges.includes(range.getA1Notation())) return;
const row = range.getRow(); // This is the row number.
const column = range.getColumn(); // This is the column number.
}
In this case, when the cell "B3" is clicked, this script is run. And row and column are the row and column number of the cell.
Because the cell is used as the button, you can put the value to the cell.
Pattern 2:
In this pattern, a checkbox is used as the button using the OnEdit trigger. When the cell "B3" is used as the button like above image, the script is as follows.
Sample script:
In this script, for example, when you want to use the cell "B3" of "Sheet1" as the button, please set "B3" to buttonRanges. And, set sheet.getSheetName() != "Sheet1". When when you want to use the cell "B3" and "B4" as the button, please set "B3" and "B4" to buttonRanges.
function onEdit(e) {
const range = e.range;
const sheet = range.getSheet();
const buttonRanges = ["B3"];
if (sheet.getSheetName() != "Sheet1" || !buttonRanges.includes(range.getA1Notation()) || !range.isChecked()) return;
const row = range.getRow(); // This is the row number.
const column = range.getColumn(); // This is the column number.
}
In this case, when the checkbox is checked, the script is run. When the checkbox is unchecked, the script is not run.
Because the checkbox is used as the button, you cannot see the text in the cell.
References:
Simple Triggers
Event Objects
Related question
Button change text display every after click
This question is for achieving the switching button.

Sitecore Update Placeholder Key (Programming)

I'd like to update the value of placeholder(PH) key assigned into each page item.
The problem is I changed the value of PH key in master template (actually combined two templates to make only one template) and a number of pages should be updated with new assigned PH key.
How to update placeholder key without clicking each item and changing the value in presentation? If I do like this, it takes a lot of time.
What I want to do in program is:
Set initial path (/sitecore/home/robot/)
Check each item (with each item's sub-item) in initial path
Retrieve each item's assigned controls in presentation
If there is "Breadcrumbs" control with "breadcrumbs" key name
Then, change the value to "/template/dynamic/breadcrumbs"
Do until it retrives all items in the initial path
See the code below. What it does, it gets rendering references for the selected items, checks their placeholders and rendering names and updates xml value of the __Renderings field of selected item, based on the unique id of selected renderings. Then it fires same code for all descendants recursively.
This code
does not update placeholders for components which are inherited from __Standard Values
does not publish changed items automatically.
is case sensitive
requires that user has write access for the items that you want to change
public void Start()
{
string initialPath = "/sitecore/home/robot";
Item root = Database.GetDatabase("master").GetItem(initialPath);
UpdatePlaceholderName(root, "Breadcrumbs", "breadcrumbs", "/template/dynamic/breadcrumbs");
}
private void UpdatePlaceholderName(Item item, string componentName, string placeholderName, string newPlaceholderName)
{
if (item != null)
{
List<RenderingReference> renderings = item.Visualization.GetRenderings(Sitecore.Context.Device, false)
.Where(r => r.Placeholder == placeholderName && r.RenderingItem.Name == componentName).ToList();
if (renderings.Any())
{
string renderingsXml = item["__Renderings"];
item.Editing.BeginEdit();
foreach (RenderingReference rendering in renderings)
{
string[] strings = renderingsXml.Split(new [] {"<r"}, StringSplitOptions.None);
foreach (string renderingXml in strings)
{
if (renderingXml.Contains("s:ph=\"" + placeholderName + "\"") && renderingXml.Contains("uid=\"" + rendering.UniqueId + "\""))
{
renderingsXml = renderingsXml.Replace(renderingXml, renderingXml.Replace("s:ph=\"" + placeholderName + "\"", "s:ph=\"" + newPlaceholderName + "\""));
}
}
}
item["__Renderings"] = renderingsXml;
item.Editing.EndEdit();
}
foreach (Item child in item.GetChildren())
{
UpdatePlaceholderName(child, componentName, placeholderName, newPlaceholderName);
}
}
}

Ember context passed to a view

I'm trying to figure out what's wrong about sending action from parentView to one of its children view:
I've made a PhotoUploadView used to resize and then upload images; it adds canvas drawing the resized image in a div inside its template:
<div class="img-list">
//here the canvas will be added
</div>
The action "saveImages" in this view, acts like this:
var list = this.$('.img-list');
$.each(list.children(), function(index, value) {
//here the code to save
}
But this action is not called directly; because I need to save not only the images but also some records (an offer record and many products records, children of the offer; the images are associated to the product record);
So I have the action "save" on the parentView that is like this:
//save records
offer.save().then(function(savedOffer) {
newProducts.forEach(function(product, indexP) {
product.set('offer', savedOffer);
product.save().then(function(savedProduct) {
}
}
}
//save photos by cycling the PhotoUploadViews that are inside ProductViews that are inside the mainView
this.get('childViews').forEach(function(view, index) {
if (index >= 4) { //the childView from the 4th are the ProductViews
var productId = view.get('product').get('id');
var folder = 'offer-' + controller.get('model').get('id') + '/product-' + productId + '/';
view.get('childViews')[0].send('saveImages', folder, productId); //the first childView of a ProductView is the UploadView
}
});
Well, this works and save the images correctly when you add images to an existing offer with existing product; but when you are creating a new offer, it fails since the folder will becone "offer-undefined/product-undefined" because of course you must wait the records to be saved in order to get their ID;
So I'm trying now to move the send action into the .then callback of product save:
var childViews = this.get('childViews'); //the childView starting from the 4th are the productsViews
offer.save().then(function(savedOffer) {
newProducts.forEach(function(product, indexP) {
product.set('offer', savedOffer);
product.save().then(function(savedProduct) {
var currentPview = (childViews[4 + indexP]); //get the productView associated with the current product
var productId = savedProduct.get('id');
var folder = 'offer-' + savedOffer.get('id') + '/product-' + productId + '/';
currentPview.get('_childViews')[2].send('saveImages', folder, productId); //the object at index 2 of _childViews array is the photoUploadView
Here, the folder is built correctly but after sending action, the saveImages action crashes saying that list is undefined; trying to log the value of "this" inside "saveImages" I can see that also its value is undefined; Someone can please explain why calling the action from one point, it works, and calling it inside the .then callback of product save it doesn't?
I also would like to understand why in the first case I can do
.get('childViews')[0]
to get the PhotoUploadView, but in the second I must do
.get('_childViews')[2]
since using get('childViews) it doesn't work anymore; What is the difference between childViews and _childViews? And why _childViews has more elements than childView?

Sitecore workbox change sort order

By default Sitecore workbox displays the Item name, and sort the item list by Item name.
In one of my previous posts regarding this I managed to change the item name to a custom field.
Now I need to sort workbox by this field. How can I do this?
Assuming that you already have your own implementation of the WorkboxForm as described in the post you linked in your question, you need to change the code of the DisplayState method.
The DataUri[] items inflow parameter of this method gives you the list of all items which are in given state of the workflows. You need to retrieve all the Sitecore items from this parameter and sort them:
DataUri[] items = new DataUri[0];
List<Item> sitecoreItems = items
.Select(uri => Context.ContentDatabase.Items[uri])
.OrderBy(item => item["YourCustomField"])
.ToList();
And use the new list for selecting the current page items. This solution is not optimized for the performance - you need to get every item in given state from database so you can access the custom field.
After studying Sitecore workbox modifications, I came across with following solution.
Step 1 - Modify the GetItems method as follows,
private DataUri[] GetItems(WorkflowState state, IWorkflow workflow)
{
if (workflow != null)
{
var items = workflow.GetItems(state.StateID);
Array.Sort(items, new Comparison<DataUri>(CompareDataUri));
return items;
}
return new DataUri[] { };
}
Here comes the "CompareDataUri" method,
private int CompareDataUri(DataUri x, DataUri y)
{
//Custom method written to compare two values - Dhanuka
Item itemX = Sitecore.Context.ContentDatabase.GetItem(x);
Item itemY = Sitecore.Context.ContentDatabase.GetItem(y);
string m_sortField = "__Updated";
bool m_descSort = false;
var res = 0;
res = string.Compare(itemX[m_sortField], itemY[m_sortField]);
if (m_descSort)
{
if (res > 0)
return -1;
if (res < 0)
return 1;
}
return res;
}
This approach is optimized for performance.