I'm trying to modify a template in TYPO3 and I can modify some parts of the page, but not some other parts that are 1 level deeper. For example :
HTML
<body>
...
<div class="wrapper">
...
<div id="content-right">
<div id="colRight">
<div id="metaNav"></div>
</div>
</div>
...
</div>
...
</body>
Typoscript
page.10.subparts {
colRight = HMENU
colRight.wrap = <ul>|</ul>
colRight.special.value = 6, 7, 8, 9
colRight.1 = TMENU
colRight.1 {
noBlur = 1
NO = 1
NO {
allWrap = <li>|</li>
}
}
}
But if I change colRight with metaNav (because this is where we want the links so we can place other contents in colRight), nothing happens; no content is displayed. Why?
While you have it mapped to #colRight and have problem with mapping it to its child div you can just add a HTML markup to element's wrap:
page.10.subparts {
colRight = HMENU
colRight.wrap = <div id="metaNav"><ul>|</ul></div>
// etc...
}
With rule #1: In TS every way is the best solution to get immediate results :)
edit
if you need to render many different elements under one HTML tag, you can also use COA element to span them:
page.10.subparts {
colRight = COA
colRight {
10 = HMENU
10 {
wrap = <div id="metaNav"><ul>|</ul></div>
// etc...
}
20 = TEXT
20 {
value = my text in #colRight right after #metaNav
wrap = <div class="containerAfterMetsNav">|</div>
}
}
}
Related
I used Gridelements a while now. I used the following code:
TypoScript (Gridelements (deprecated) (gridelements)):
tt_content.gridelements_pi1.20.10.setup {
2col < lib.gridelements.defaultGridSetup
2col.cObject = FLUIDTEMPLATE
2col.cObject.file = {$resDir}/Private/Partials/Gridelements/2spalten.html
}
FLUID Template:
<div class="row">
<div class="col-md-6">
{data.tx_gridelements_view_column_0}
</div>
<div class="col-md-6">
{data.tx_gridelements_view_column_1}
</div>
</div>
PageTS:
tx_gridelements.setup {
2col {
title = Two Columns
config {
colCount = 2
rowCount = 1
rows {
1 {
columns {
1 {
name = Links
colPos = 0
}
2 {
name = Rechts
colPos = 1
}
}
}
}
}
}
}
But now I want to use "Gridelements w/DataProcessing (recommended) (gridelements)" because the other one is deprecated. But all I see is the error:
Tried resolving a template file for controller action "Standard->2col"
in format ".html", but none of the paths contained the expected
template file (Standard/2col.html). The following paths were checked:
/var/www/html/typo3/sysext/fluid_styled_content/Resources/Private/Templates/,
/var/www/html/typo3/typo3conf/ext/gridelements/Resources/Private/Templates/,
/var/www/html/typo3/typo3conf/ext/dev_layout/Resources/Private/Templates/
If I write this in my TypoScript code:
lib.gridelements.defaultGridSetup =< lib.contentElement
lib.gridelements.defaultGridSetup {
templateRootPaths {
20 = {$resDir}/Private/Templates/Gridelements/
}
}
tt_content.gridelements_pi1 < lib.gridelements.defaultGridSetup
tt_content.gridelements_view < tt_content.gridelements_pi1
And when I create the named file, the error no longer appears. But there is no output. I see the divs but no content. How can I switch from deprecated gridelements to dataprocessing gridelements?
"The documentation is wrong you need this"
tt_content.gridelements_pi1 =< lib.contentElement
Sure? I could still use the original as it is written in the example for w / DataProcessing:
lib.gridelements.defaultGridSetup =< lib.contentElement
The problem is that this code
tt_content.gridelements_pi1.20.10.setup {
2col < lib.gridelements.defaultGridSetup
2col.cObject = FLUIDTEMPLATE
2col.cObject.file = {$resDir}/Private/Partials/Gridelements/2spalten.html
}
does not match the new static you included.
Take a look at the basic example shown in the documentation:
https://docs.typo3.org/typo3cms/extensions/gridelements/stable/Chapters/DataProcessing/Index.html
lib.gridelements.defaultGridSetup =< lib.contentElement
lib.gridelements.defaultGridSetup {
templateName.field = tx_gridelements_backend_layout
templateName.ifEmpty = GridElement
layoutRootPaths {
1 = EXT:gridelements/Resources/Private/Layouts/
}
partialRootPaths {
1 = EXT:gridelements/Resources/Private/Partials/
}
templateRootPaths {
1 = EXT:gridelements/Resources/Private/Templates/
}
dataProcessing {
10 = GridElementsTeam\Gridelements\DataProcessing\GridChildrenProcessor
10 {
default {
as = children
# Default options of the grid children processor
# Change them according to the needs of your layout
# Read more about it in the TypoScript section of the manual
# options {
# sortingDirection = ASC
# sortingField = sorting
# recursive = 0
# resolveFlexFormData = 1
# resolveBackendLayout = 1
# respectColumns = 1
# respectRows = 1
# }
}
}
}
}
Now if you want to add your own templates and rendering configurations you just need to add something in dataProcessing.10 like
dataProcessing {
10 = GridElementsTeam\Gridelements\DataProcessing\GridChildrenProcessor
10 {
2col {
as = columncontent
options {
resolveFlexFormData = 0
respectRows = 0
}
}
accordion {
as = accordionitems
options {
resolveFlexFormData = 0
respectRows = 0
respectColumns = 0
}
}
}
}
The name of the variables can still be children, but it might be more comfortable to deal with variable names matching then purpose of your template.
I found the solution:
Template:
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<div class="row">
<f:if condition="{children}">
<f:for each="{children.1}" as="column" key="columnNumber">
<div id="c{data.uid}-{columnNumber}" class="grid-column grid-column-{columnNumber} col-lg-6">
<f:for each="{column}" as="child">
<f:render partial="Child"
arguments="{data: child.data, children: child.children, options: options, settings: settings}" />
</f:for>
</div>
</f:for>
</f:if>
</div>
</html>
For two different cols
<div id="c{data.uid}-{columnNumber}" class="grid-column grid-column-{columnNumber} {f:if(condition: '{columnNumber} == 0', then: 'col-lg-3', else: 'col-lg-9')}">
My column numbers are left 0 and right 1
TS in dataprocessing.10:
2col {
as = children
options {
resolveChildFlexFormData = 0
}
}
}
The documentation is wrong you need this
tt_content.gridelements_pi1 =< lib.contentElement
TSconfig:
Default gridelements TSConfig
I got same error after i upgrade to latest TYPO3 10 version from Typo3 9 and include Gridelement (Recommended).
Tried resolving a template file for controller action "Standard->1" in format ".html", but none of the paths contained the expected template file (Standard/1.html). The following paths were checked:
Solution:
Rename gridelement template name with 1.html for gridelement uid=1
Change TS like:
tt_content.gridelements_pi1 {
templateRootPaths {
15 = EXT:site_config/Resources/Private/Templates/Extensions/Grid-Templates/
}
dataProcessing {
10 {
default {
options {
resolveFlexFormData = 1
resolveChildFlexFormData = 0
}
}
}
}
}
Inside Grid-Templates add your gridelement templates like 1.html, 2.html etc.
3) change template(HTML) code like this:
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<div class="row">
<f:if condition="{children}">
<f:for each="{children.1}" as="column" key="columnNumber">
<div id="c{data.uid}-{columnNumber}" class="grid-column grid-column-{columnNumber} col-lg-6">
<f:for each="{column}" as="child">
<f:render partial="Child"
arguments="{data: child.data, children: child.children, options: options, settings: settings}" />
</f:for>
</div>
</f:for>
</f:if>
</div>
</html>
I am trying to speed up my script, right now I currently have it set up so that on a button click a custom dialog (HTML) appears asking some questions. On submit, it calls out a gs function to pull the info back as variables. Depending on the first answer I have a series of If statments that trigger. Each of them pull up a different template, make a copy, populate some cells, emails it to you, and then dumps the data into a tracker. Each of them are different so the script is rather long - is there a way to have each if statment its own function? Would this even help the speed? I am new to scripting so any feedback is appreciated. Below is the Code and HTML
function onOpen() //adds option to top row in case buttons are not working.
{
var ui = SpreadsheetApp.getUi();
ui.createMenu('Create Doc.')
.addItem('Create Tracked Document', 'addItem')
.addToUi();
}
function addItem()//starts the initiation process
{
var html = HtmlService.createTemplateFromFile('form')
.evaluate()
.setWidth(300)
.setHeight(550);
SpreadsheetApp.getUi()
.showModalDialog(html, 'Create New Document');
}
function addNewItem(form_data)//pulls data from form
{
var ui = SpreadsheetApp.getUi();
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var n = new Date();
var now = ((n.getMonth()+1) + "/" + n.getDate() + "/" + n.getFullYear());
var doctyp = form_data.Document_Type;
var name = form_data.Name;
var title = form_data.Title;
var platform = form_data.Platform;
var area = form_data.Area;
var rota = form_data.Rotation;
var works = form_data.WorkSt;
var recipient = Session.getEffectiveUser().getEmail();
if (form_data.Document_Type == "Text2"){
var dumpfolder = DriveApp.getFolderById("12345")
var templateSheet = DriveApp.getFileById("67890");
var Newform2= templateSheet.makeCopy(title+ " "+now,dumpfolder);
var qs = SpreadsheetApp.open(Newform2);
var dropSheet = qs.getSheetByName("blank");
var URL3 = Newform2.getUrl();
dropSheet.getRange("i8").setValue(title);
dropSheet.getRange("bc5").setValue(now);
dropSheet.getRange("b5").setValue(platform);
dropSheet.getRange("p5").setValue(area);
dropSheet.getRange("x5").setValue(rota);
dropSheet.getRange("al5").setValue(works);
dropSheet.getRange("at6").setValue(name);
NewOPLPOA.setSharing(DriveApp.Access.DOMAIN,DriveApp.Permission.COMMENT);
NewOPLPOA.setOwner("ME");
sheet.appendRow([now,doctyp,name,title,platform,area,rota,works,URL3]);
GmailApp.sendEmail(recipient, title+ " has been created.", "Your document has been created." +'\n'+ "Here is the link to your copy! Link: " + URL3);
ui.alert("Email Sent", "An email has been sent with your documents link. You can also use the below link to view the document now, click ctrl C to copy. \
" + URL3, ui.ButtonSet.OK);
}
else if (form_data.Document_Type == "Text1"){
var dumpfolder = DriveApp.getFolderById("abcd")
var templateSheet = DriveApp.getFileById("bgtrd");
var Newform1 = templateSheet.makeCopy(title+ " "+now,dumpfolder);
var qs = SpreadsheetApp.open(Newform1);
var dropSheet = qs.getSheetByName("DOC1");
var URL4 = Newform1.getUrl();
dropSheet.getRange("aa3").setValue(platform);
dropSheet.getRange("ah3").setValue(area);
dropSheet.getRange("ao3").setValue(works);
NewSEWO.setSharing(DriveApp.Access.DOMAIN, DriveApp.Permission.COMMENT);
NewSEWO.setOwner("Me");
sheet.appendRow([now,doctyp,name,title,platform,area,rota,works,URL4]);
GmailApp.sendEmail(recipient, title+ " has been created.", "Your document has been created." +'\n'+ "Here is the link to your copy! Link: " + URL4);
ui.alert("Email Sent", "An email has been sent with your documents link. You can also use the below link to view the document now, click ctrl C to copy. \
" + URL4, ui.ButtonSet.OK);
}
else{
ui.alert("Error, Please try again, make sure you are listing all required information.");
}
}
--HTML--
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
</head>
<body>
<form id="myform">
<div class="form-group">
<label for="Document_Type">Document Type</label>
<select class="form-control" id="Document_Type" name = "Document_Type" required="required">
<?
var SS = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Set Up");
var Avals = SS.getRange("A2:A").getValues();
var numberOfValues = Avals.filter(String).length;
var RangeVals = SS.getRange(2,1,numberOfValues).getValues();
?>
<option disabled selected value> -- select an option -- </option>
<? for (var i = 0; i < RangeVals.length; ++i) { ?>
<option><?!= RangeVals[i] ?></option>
<? } ?>
</select>
</div>
<div class="form-group">
<label for ="Name">Your Name</label>
<select class="form-control" name='Name' id="Name" required="required">
<?
var AvalN = SS.getRange("E2:E").getValues();
var numberOfValuesN = AvalN.filter(String).length;
var RangeValsN = SS.getRange(2,5,numberOfValuesN).getValues();
?>
<option disabled selected value> -- select an option -- </option>
<? for (var i = 0; i < RangeValsN.length; ++i) { ?>
<option><?!= RangeValsN[i] ?></option>
<? } ?>
</select>
</div>
<div class="form-group">
<label for="Platform">Platform</label>
<select class="form-control" id="Platform" name = "Platform" required="required">
<?
var AvalP = SS.getRange("C2:C").getValues();
var numberOfValuesP = AvalP.filter(String).length;
var RangeValsP = SS.getRange(2,3,numberOfValuesP).getValues();
?>
<option disabled selected value> -- select an option -- </option>
<? for (var i = 0; i < RangeValsP.length; ++i) { ?>
<option><?!= RangeValsP[i] ?></option>
<? } ?>
</select>
</div>
<div class="form-group">
<label for="Area">Area</label>
<select class="form-control" id="Area" name = "Area" required="required">
<?
var AvalA = SS.getRange("D2:D").getValues();
var numberOfValuesA = AvalA.filter(String).length;
var RangeValsA = SS.getRange(2,4,numberOfValuesA).getValues();
?>
<option disabled selected value> -- select an option -- </option>
<? for (var i = 0; i < RangeValsA.length; ++i) { ?>
<option><?!= RangeValsA[i] ?></option>
<? } ?>
</select>
</div>
<div class="block form-group">
<label for="Rotation">Rotation</label>
<select class="form-control" name='Rotation' id="Rotation">
<?
var AvalR = SS.getRange("F2:F").getValues();
var numberOfValuesR = AvalR.filter(String).length;
var RangeValsR = SS.getRange(2,6,numberOfValuesR).getValues();
?>
<option disabled selected value> -- select an option -- </option>
<? for (var i = 0; i < RangeValsR.length; ++i) { ?>
<option><?!= RangeValsR[i] ?></option>
<? } ?>
</select>
</div>
<div class="block form-group">
<label for="WorkSt">Work Station</label>
<input type='text' name='WorkSt' id="WorkSt" />
</div>
<div class="block form-group">
<label for="Title">Title</label>
<input type='text' name='Title' id="Title" required="required"/>
</div>
<div class="block">
<button type="submit" class="action">Submit</button>
</div>
</form>
<script>
document.querySelector("#myform").addEventListener("submit",
function(e)
{
e.preventDefault(); //stop form from submitting
google.script.run.addNewItem(this);
google.script.host.close();//close this dialogbox
}
);
</script>
</body>
</html>
is there a way to have each if statement its own function?
For visibility purposes, you certainly can modify your code as
function addNewItem(form_data)//pulls data from form
{
var ui = SpreadsheetApp.getUi();
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
var n = new Date();
var now = ((n.getMonth()+1) + "/" + n.getDate() + "/" + n.getFullYear());
var doctyp = form_data.Document_Type;
var name = form_data.Name;
var title = form_data.Title;
var platform = form_data.Platform;
var area = form_data.Area;
var rota = form_data.Rotation;
var works = form_data.WorkSt;
var recipient = Session.getEffectiveUser().getEmail();
if (form_data.Document_Type == "Text2"){
function1();
}
else if (form_data.Document_Type == "Text1"){
function2();
}
else{
ui.alert("Error, Please try again, make sure you are listing all required information.");
}
function function1(){
var dumpfolder = DriveApp.getFolderById("12345")
var templateSheet = DriveApp.getFileById("67890");
...
}
function function2(){
var dumpfolder = DriveApp.getFolderById("abcd")
...
}
Would this even help the speed?
Not really. To help up speed, you should rather try to implement Best Practices.
In particular: Reduce repeated calls to external services, including SpreadsheetApp.
For example, try to position the cells to which you want to assign values into an adjacent range, so you can use the method setValues() instead of multiple setValue() and thus make your code more efficient.
Sample:
var range = dropSheet.getRange("I8:N8");
var values = [];
values[0] = [];
values[0].push(title, now, platform, area, rota, works, name);
range.setValues(values);
Also, try to avoid repeating the same request for each if condition and rather make a single request after exiting the if statement, e.g. for:
sheet.appendRow([now,doctyp,name,title,platform,area,rota,works,URL3]);
GmailApp.sendEmail(recipient, title+ " has been created.", "Your document has been created." +'\n'+ "Here is the link to your copy! Link: " + URL3);
ui.alert("Email Sent", "An email has been sent with your documents link. You can also use the below link to view the document now, click ctrl C to copy. \
" + URL3, ui.ButtonSet.OK);
I hope this helps!
I have an HMENU with a submenu and I want to add a third submenu, if the main menu point has the uid xxx.
If I implement this TypoScript Code, all third submenus will be shown:
3 = TMENU
3 {
stdWrap.outerWrap = <div class="submenu-third-level"><ul class='submenu'>|</ul></div>
stdWrap.outerWrap.override = <div class="submenu-third-level show"><ul class='submenu'>|</ul></div>
stdWrap.outerWrap.override.if {
value.data = field:pid
isInList = 588
}
stdWrap.insertData = 1
NO.wrapItemAndSub = <li class="menu-item">|</li>
ACT = 1
ACT{
wrapItemAndSub = <li class="menu-item active">|</li>
}
SPC = 1
SPC {
doNotLinkIt = 1
doNotShowLink = 1
allWrap = </ul><ul class='submenu'>
}
}
Thus, all submenus of submenus will be shown. But I want to only show the submenus of submenus in HMENU PID XXX.
Is there a possibility to do it like:
3 = TMENU
3 {
stdWrap.outerWrap = <div class="submenu-third-level"><ul class='submenu'>|</ul></div>
stdWrap.outerWrap.override = <div class="submenu-third-level show"><ul class='submenu'>|</ul></div>
stdWrap.outerWrap.override.if {
value.data = field:pid
isInList = 588
}
stdWrap.insertData = 1
NO.wrapItemAndSub = <li class="menu-item">|</li>
ACT = 1
ACT{
wrapItemAndSub = <li class="menu-item active">|</li>
}
SPC = 1
SPC {
doNotLinkIt = 1
doNotShowLink = 1
allWrap = </ul><ul class='submenu'>
}
if {
value.data = field:pid
equals = xxx
}
}
Look you better use new HMENU like:
lib.mainmenu = HMENU
...{
1 = TMENU
...
2 = TMENU
} # so just two levels
lib.tempmenu <. lib.mainmenu # just save your menu
[PIDinRootline = xxx]
#or [globalVar = TSFE:id=xxx]
lib.mainmenu <. lib.tempmenu
lib.mainmenu.3 = TMENU # just add 3d submenu. Prev menu haven't it
[global]
If not, please leave comment
thanks for your response. I solved in a different way and it works great.
My solution:
3 = TMENU
3 {
stdWrap.outerWrap = <div class="submenu-third-level"><ul class='submenu'>|</ul></div>
stdWrap.if {
value.data = field:pid
isInList = {$menu.thirdSubmenuList}
}
NO.wrapItemAndSub = <li class="menu-item">|</li>
ACT = 1
ACT{
wrapItemAndSub = <li class="menu-item active">|</li>
}
SPC = 1
SPC {
doNotLinkIt = 1
doNotShowLink = 1
allWrap = </ul><ul class='submenu'>
}
}
The condition decide, if the menu will be displayed or not.
best regards
I would like to combine two menu's into one for mobile. So with the foundation top-bar the standaard menu is shown and the below the tip menu into one menu. But i don't get the tip menu wrapped into the main menu just before the first . See code below, any idee?
topnavigation = HMENU
topnavigation.wrap (
<section class="topnavigation">
<div class="row">
<div class="columns large-12">
<nav class="top-bar" data-topbar data-options="back_text: « Vorige">
<ul class="title-area">
<li class="name">
<h1></h1>
</li>
<li class="toggle-catmenu show-for-small menu-icon">Tips</li>
<li class="toggle-topbar menu-icon"><span>Menu</span></li>
</ul>
<section class="top-bar-section">
<ul class="right">|<li class="divider"></li></ul>
</section>
</nav>
</div>
</div>
</section>
)
topnavigation.entryLevel = 0
topnavigation {
1= TMENU
1 {
expAll = 1
maxItems = 4
NO.wrapItemAndSub = <li class="top-but">|</li>
ACT = 1
ACT.wrapItemAndSub = <li class="active top-but">|</li>
IFSUB = 1
IFSUB.wrapItemAndSub = <li class="has-dropdown top-but">|</li>
ACTIFSUB = 1
ACTIFSUB.wrapItemAndSub= <li class="active has-dropdown top-but">|</li>
}
2= TMENU
2 {
wrap = <ul class="dropdown">|</ul>
NO.wrapItemAndSub = <li>|</li>
ACT = 1
ACT.wrapItemAndSub = <li class="active">|</li>
}
}
tipmenu = HMENU
tipmenu.special = directory
tipmenu.special.value = 8
tipmenu.allWrap = <ul class="left">|</ul>
tipmenu {
1 = TMENU
1 {
expAll = 1
maxItems = 4
NO.wrapItemAndSub = <li class="top-but">|</li>
ACT = 1
ACT.wrapItemAndSub = <li class="active top-but">|</li>
IFSUB = 1
IFSUB.wrapItemAndSub = <li class="has-dropdown top-but">|</li>
ACTIFSUB = 1
ACTIFSUB.wrapItemAndSub= <li class="active has-dropdown top-but">|</li>
}
2= TMENU
2 {
wrap = <ul class="dropdown">|</ul>
NO.wrapItemAndSub = <li>|</li>
ACT = 1
ACT.wrapItemAndSub = <li class="active">|</li>
}
}
You can't combine one HMENU inside the other instead you need to use COA cObject, COA allows for combining many cObjects (even different type):
myCombinedMenu = COA
myCombinedMenu.10 < lib.mainMenu
myCombinedMenu.20 < lib.additionalMenu
myCombinedMenu.30 = TEXT
myCombinedMenu.30.value = ...and that's it...
This should do it. COA is not necessary. This solution has a common parent ul tag and displays both sub tree items (the items of two different sub-trees) at the same ul level:
temp.mainMenuObject = HMENU
temp.mainMenuObject {
# entryLevel = 1
special = directory
special.value = pid1, pid2 # pids of parent pages
1 = TMENU
1 {
expAll = 1
wrap = <ul> | </ul>
NO = 1
NO {
wrapItemAndSub = <li>|</li>
ATagTitle.field = title
}
}
2 < .1
}
I'm trying to filter strings in a static unordered list. I'm able to filter items in an array but not sure how to do it for static html content.
The way I've learned to achieve it using an array is by using this method:
<input type="search" ng-model="name" />
<ul>
<li ng-repeat="person in people | filter:name">
{{ person }}
</li>
</ul>
I'm trying to achieve the same effect using an existing <ul>
<input type="search" ng-model="filter.name" placeholder="filter..." />
<ul>
<li>Bob/li>
<li>Lisa</li>
<li>Lewis</li>
<li>Xuemin</li>
<li>Tom</li>
<li>Cassidy</li>
</ul>
I want to be able to filter the list items based on the text strings inside them, so for example typing 'L' in the textbox would only show:
Lisa
Lewis
Here is a hacked out way of doing it JSFiddle.
The key to it is in ng-change="filter()". Every time you enter a letter you decide which elements to show or hide.
$scope.filter = function() {
var elem = document.getElementById('list');
for (var i = 0; i < elem.children.length; i++) {
var name = elem.children[i].children[0].innerHTML;
if (name.indexOf($scope.name) != -1 || $scope.name == "") {
elem.children[i].style.display = "block";
} else {
elem.children[i].style.display = "none";
}
}
}
Like I said, it's an ugly hack, but it works. You would be better off making an object out of your names, and using ng-repeat.