Google Apps Script trigger code only runs for primary calendar, not for others

Good morning,

I’ve implemented the provided code, and it works as expected when I make changes to an event in my primary calendar. However, when I try to modify an event in another calendar that I’ve created, the code doesn’t run. This issue occurs because the ‘owner’ variable is not correctly defined or is empty in my code.

// Cette fonction met à jour les titres des événements dans un calendrier.
function updateEventTitle(e) {

    // Obtenez l'ID du calendrier depuis l'objet de déclenchement.
    var calendarId = e.calendarId;
    // Obtenez le calendrier par son ID.
    var calendar = CalendarApp.getCalendarById(calendarId);

    Logger.log('calendarId' + calendarId);
    // Obtenez les propriétés de l'utilisateur pour stocker le jeton de synchronisation.
    var properties = PropertiesService.getUserProperties();
    var options = {};

    // Récupérez le jeton de synchronisation.
    var syncToken = properties.getProperty('syncToken');
    if (syncToken) {
        // Ajoutez le jeton de synchronisation aux options.
        options.syncToken = syncToken;
    } else {
        // Sinon, synchronisez les événements à partir de la date actuelle.
        options.timeMin = new Date().toISOString();
    }

    var eventResponse;
    var pageToken = null;

    try {
        do {
            // Liste des événements avec les options de synchronisation.
            var eventResponse = Calendar.Events.list(calendarId, options);
            pageToken = eventResponse.nextPageToken;

            if (eventResponse.nextSyncToken != null) {
                // Mettez à jour le jeton de synchronisation.
                options.syncToken = eventResponse.nextSyncToken;
                properties.setProperty('syncToken', eventResponse.nextSyncToken);
            }

            var events = eventResponse.items;
            for (var j = 0; j < events.length; j++) {
                // Obtenez la description du calendrier.
                var descriptionCalendrier = calendar.getDescription();
                // Obtenez un événement par son ID.
                var event = calendar.getEventById(events[j].id);
                // Obtenez le titre de l'événement.
                var title = event.getTitle();
                // Obtenez la description de l'événement.
                var description = event.getDescription();
                // Récupérez la liste des participants de l'événement.
                var participants = event.getGuestList(true);

                // Récupérez le nombre de participants maximum (limite).
                var limitEventMatch = description.match(/limites*:s*(d+)/i);
                var limitCalendrierMatch = descriptionCalendrier.match(/limites*:s*(d+)/i);
                if(limitEventMatch) {
                    limitparticipant = limitEventMatch[1];
                    //Logger.log('limitEventMatch' + limitEventMatch[1]);
                } else {
                    if(limitCalendrierMatch) {
                        limitparticipant = limitCalendrierMatch[1];
                        //Logger.log('limitCalendrierMatch' + limitCalendrierMatch[1]);
                    } else {
                        //Logger.log('aucun');
                        var limitparticipant = 1;
                    }
                }

                Logger.log('event'+event)
                // Obtenez l'adresse e-mail de l'organisateur (créateur) de l'événement.
                const guestListWithoutOwner = event.getGuestList(); // get the guest list without the owner
                // Comptez le nombre de participants.
                var numberOfParticipants = guestListWithoutOwner.length;
                // filter the guest list to ingore duplicate emails leaving only the owner
                // based on https://stackoverflow.com/a/42995029/1027723, https://pulse.appsscript.info/p/2022/04/getting-a-google-calendar-event-owner-using-google-apps-script/
                Logger.log('guestListWithoutOwner'+guestListWithoutOwner)
                var owner = participants.filter(o1 => !guestListWithoutOwner.some(o2 => o1.getEmail() === o2.getEmail()));
                Logger.log('owner'+owner[0]);
                var organizerEmail = owner[0].getEmail();
                Logger.log('organizerEmail'+organizerEmail);

                // Savoir si l'organisateur participe
                organisateurParticipe = 'YES';
                Logger.log('participants'+participants[i]);
                for (var i = 0; i < participants.length; i++) {
                  Logger.log(participants[i].getEmail());
                  if (participants[i].getEmail() == organizerEmail) {
                      if(participants[i].getGuestStatus() == 'NO') {
                          organisateurParticipe = false;
                      }
                      break;
                  }
                }



                /*
                Atelier
                */
                // Récupérez si il y a ANNULÉ dans le titre.
                if (title.indexOf("Atelier") !== -1 && !limitEventMatch && !limitCalendrierMatch) {
                    limitparticipant = 10; // Définissez la limite à 10 s'il y a "Atelier" dans le titre et pas de limite définie.
                    description += "nlimite : " + limitparticipant; // Ajoutez la limite dans la description.
                    event.setDescription(description); // Mettez à jour la description de l'événement.
                }            



                /*
                NOMBRE DE PARTICIPANTS
                */

                // Récupérez le nombre d'organisateurs maximum (limite).
                var organisateurMatch = description.match(/organisateurs*:s*(d+)/i);
                var limitorganisateur = organisateurMatch ? organisateurMatch[1] : 1;

                // Recherche le compteur existant dans le titre.
                var titleMatch = title.match(/(d+/d+)/);


                if (numberOfParticipants === 0) {
                    // Si aucun participant, retirez le compteur.
                    title = titleMatch ? title.replace(titleMatch[0], '') : title;
                } else {
                    // Calculez le nombre de participants en soustrayant le nombre d'organisateurs.
                    var participants = parseInt(numberOfParticipants) - parseInt(limitorganisateur); // -1 on retire l'organisateur
                    // Mettez à jour le titre avec le nombre de participants et la limite.
                    // Si le compteur existe déjà dans le titre, je le mets à jour, sinon je l'ajoute
                    title = titleMatch
                        ? title.replace(titleMatch[0], '(' + participants + '/' + limitparticipant + ')')
                        : title + ' (' + participants + '/' + limitparticipant + ')';
                }


                /*
                ANNULÉ
                */

               // Récupérez la couleur initiale
                var couleurMatch = description.match(/couleurs*:s*(d+)/i);

                if (couleurMatch) {
                    var couleurInitiale = parseInt(couleurMatch[1]);
                } else {
                    // Si la couleur initiale n'est pas définie, obtenez la couleur actuelle de l'événement
                    var couleurInitiale = event.getColor() ? event.getColor() : 0;
                    // Ajoutez la couleur à la description
                    description += "ncouleur : " + couleurInitiale;
                    // Mettez à jour la description de l'événement
                    event.setDescription(description);
                }

                // Récupérez si il y a ANNULÉ dans le titre.
                var annuleMatch = /ANNULÉ /.test(title);

                if (!organisateurParticipe) {
                    if(!annuleMatch) {
                        title = 'ANNULÉ ' + title;
                    }
                    event.setColor(CalendarApp.EventColor.GRAY);
                } else {
                    title = title.replace('ANNULÉ ', '');
                    // Restaurez la couleur initiale
                    event.setColor(couleurInitiale);
                }


                // Mettez à jour le titre de l'événement.
                event.setTitle(title);

            }

            options.nextPageToken = pageToken;

        } while (pageToken != null);
    } catch (ex) {
        // Gérez les exceptions s'il y en a.
        Logger.log('Exception: %s', ex);
    }
}

I attempted alternatives solutions, such as the one below, but it did not yield the expected results:

const guestListWithoutOwner = event.getGuestList() || [];
var numberOfParticipants = guestListWithoutOwner.length;

if (numberOfParticipants > 0) {
    var owner = participants.filter(o1 => !guestListWithoutOwner.some(o2 => o1.getEmail() === o2.getEmail()));
    if (owner.length > 0) {
        var organizerEmail = owner[0].getEmail();
    }
} else {
    // Handle the scenario when there are no participants (or no owner) if necessary.
}

Do you have any insights into why this might be happening?

Thank you very much,
Guillaume.

How to Automatically Convert Markdown (.md) to HTML and Render it in a Specific Element using Node.js and markdown-it?

I’m working on a project where my customer wants to be able to type Markdown (MD) content in a .md file and have it automatically converted to HTML. The generated HTML should then be displayed in a specific element within an HTML file.

I’ve set up a Node.js project and installed the necessary packages (markdown-it and fs). Here’s the code I’m using in my convert.js file:

const fs = require('fs');
const markdownIt = require('markdown-it');

const mdContent = fs.readFileSync('ru/input.md', 'utf-8');

const htmlContent = markdownIt.render(mdContent);

const divElement = document.querySelector('.vlog');
divElement.innerHTML = htmlContent;
<main>
    <div class="vlog">

    </div>
</main>

However, when I type anything in the .md file, it doesn’t seem to automatically convert the content. I’ve attached a screenshot of my project structure for reference.

enter image description here

What could be causing this issue, and how can I make it work as intended? Any help or insights would be greatly appreciated.

SAP UI5 Dynamic id for core:HTML appexcharts div + synchronization issue

I have a ui5 application that contains appexcharts, I have faced two issues so far,

The first one is that in some cases I have dynamic number of charts that should be added based on oData number of records, so I tried this

View:

<List id="donutList" items="{myList>/}">
  <CustomListItem>
     <HBox>
        <core:HTML></core:HTML>
     </HBox>
  <CustomListItem>
</List>

Controller:

var listItems = this.getView().byId("donutList").getItems();
var oModel = this.getView().getModel("myList");
for(var i =0; i < listItems.length; i++) {
    var sPath = listItems[i].getBindingContextPath();
    var item = oModel.getProperty(sPath);
    listItems[i].getContent()[0].getItems()[0].setContent("<div id='donutChart" + item.id + "'></div>");
}

when I use the setContent method it does set the content but when I try onAfterRendering it always gives me an error that it is null, for example this line returns null

console.log(document.querySelector("#donutChart" + item.id ));

so, is there a way where I can write the dynamic id in the xml? is there a syntax to add dynamic id to this line? I’ve searched allot and didn’t find a result that works

<core:HTML content=“&lt;div id=&quot; ID-HERE &quot; &gt;&lt;/div&gt;”></core:HTML>

My Second question is that I have synchronization issue with appexcharts, 9 of 10 times the afterRendering function is executed before the oData retrieved from the backend, although I’m adding the afterRendering function inside the success part of oData read.

that.getView().addEventDelegate({
    onAfterRendering: that.onRenderingAppex()
}, that);

I tried the async await but it created more issues than it solved. is there another way to make sure that the oData comes before the afterRendering excuted?

Sorry for the so many questions but I have been trying to solve this for two weeks without succeeding.
and thank you in advance.

React.js – Preventing Multiple Image Uploads on File Input Change

I’m working on a React.js application that allows users to send messages with text and images. I have an input field for text and a hidden file input field for selecting images. The issue I’m facing is that when I select an image, it gets uploaded twice, even though I intend to send it only once. How can I prevent this double upload behavior?

import React, { useContext, useState } from "react";
// ... (other imports and context setup) ...

const Input = () => {
  const [text, setText] = useState("");
  const [img, setImg] = useState(null);

  // ... (context and user data setup) ...

  const handleSend = async () => {
    if (img) {
      const storageRef = ref(storage, uuid());

      const uploadTask = uploadBytesResumable(storageRef, img);

      uploadTask.on(
        (error) => {
          console.log(error);
        },
        () => {
          getDownloadURL(uploadTask.snapshot.ref).then(async (downloadURL) => {
            await updateDoc(doc(db, "chats", data.chatId), {
              messages: arrayUnion({
                id: uuid(),
                senderId: currentUser.uid,
                date: Timestamp.now(),
                img: downloadURL,
              }),
            });
          });
        }
      );
    } else {
      // ... (text message handling) ...
    }

    // ... (update database and reset input fields) ...
  };

  return (
    <div className="input">
      <input
        disabled={isDisabled}
        type="text"
        placeholder="Type something..."
        onChange={(e) => setText(e.target.value)}
        value={text}
      />
      <div className="send">
        <label htmlFor="file">
          <img src="https://img.icons8.com/fluency/48/image--v1.png" alt="" />
          <input
            disabled={isDisabled}
            type="file"
            style={{ display: "none" }}
            id="file"
            onChange={(e) => setImg(e.target.files[0])}
          />
        </label>
        <button onClick={handleSend}>Send</button>
      </div>
    </div>
  );

  // ... (export and additional details) ...
};

I suspect that the onChange event for the file input might be triggering twice, causing the image to be uploaded twice. How can I resolve this issue and ensure that the image is uploaded only once when I select it?

Any help or suggestions would be greatly appreciated.

Dokan error js/jquery – no classes in body

i was working on my website funcionality and i messed something up. I have noticed that dokan is not adding the the classes to HTML body (for example .dokan-dashboard). The rest of the website seems to work without any issues. Dokan was working just fine aswell few days ago. Any ideas what could cause this kind of issues?

(https://i.stack.imgur.com/2zHxf.jpg)

I have tried reinstalling the plugin, wordpress, theme – nothing helped.

How do I generate a google line chart with php and mysql?

I would like to generate a multiline google chart that uses data from my database.

I have used to code below. It works fine when I have just one entry in my database but the moment i add a second entry the webpage is blank.

Result if I have one entry in my database. If I add a second the code stops working and the webpage becomes blank.

enter image description here

<?php include("db.php"); ?>
<html>
<head>
  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
  <script type="text/javascript">
    google.charts.load('current', {
      'packages': ['corechart']
    });
    google.charts.setOnLoadCallback(drawChart);

    function drawChart() {
      var data = google.visualization.arrayToDataTable([
        ['id', 'tq', 'np', 'tit', 'ng', 'ff', 'eot', 'eop', 'sdg'],

        <?php
        $query = "select * from engine";
        $res = mysqli_query($conn, $query);
        while ($data = mysqli_fetch_array($res)) {

          $id = $data["id"];
          $tripid = $data["tripid"];
          $tq = $data["tq"];
          $np = $data["np"];
          $tit = $data["tit"];
          $ng = $data["ng"];
          $ff = $data["ff"];
          $eot = $data["eot"];
          $eop = $data["eop"];
          $sdg = $data["sdg"];


        ?>

          ['<?php echo $id; ?>', <?php echo $tq; ?>, <?php echo $np; ?>, <?php echo $tit; ?>, <?php echo $ng; ?>, <?php echo $ff; ?>, <?php echo $eot; ?>, <?php echo $eop; ?>, <?php echo $sdg; ?>]

        <?php }

        ?>

      ]);

      var options = {
        title: 'Engine Monitor & Reliability Log',
        // curveType: 'function',
        legend: {
          position: 'bottom'
        }
      };

      var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));

      chart.draw(data, options);
    }
  </script>
</head>

<body>
  <div id="curve_chart" style="width: 900px; height: 500px"></div>
</body>

</html>

How do I scrollIntoView a DOM Range?

The Element interface offers a scrollIntoView method that sets up the scroll positions of all ancestor elements so that the given element is visible on the screen.

How can I perform an analogous operation with a DOM Range?

I can probably live with manually computing the bounding box of the range and finding which element to scroll, but I do not wish to modify the contents of the DOM in any way.

Is there a way to write a typeguard against a complex combinatory type that preserves previous type information?

Is there a way to write a TypeScript typeguard against a complex combinatory type (that uses many “and” and “or” statements) to test the existence of one of the “or” types? Important to note that the return type of the typeguard should assert that the incoming type is exactly the same as itself (meaning that if there were any typeguard calls made prior on this type, that this filtered type information gets preserved) plus the “or” type that was checked for.

For example:

interface Type1 { cat: string }
interface Type2 { dog: boolean }
interface Type3 { mouse: number }
interface Type4 { elephant: string | number }
type CombinatoryType = (Type1 & (Type2 | Type3)) | Type4;

If we have a typeguard to check the existence of Type2 and Type4 (called hasType2 and hasType4 respectively), calling them in the following order should produce the following results:

function doSomething(data: CombinatoryType) {
    if (hasType2(data)) {
        // 'data' is now of type: (Type1 & Type2) | Type4

        if (hasType4(data)) {
            // 'data' is now of type: Type1 & Type2 & Type4
       }
    }
}

And calling them in the opposite order should produce the following results:

function doSomething(data: CombinatoryType) {
    if (hasType4(data)) {
        // 'data' is now of type: (Type1 & (Type2 | Type3)) & Type4

        if (hasType2(data)) {
            // 'data' is now of type: Type1 & Type2 & Type4
       }
    }
}

How can I shorten this to 1 line?

I want to change my below 3 lines to just 1 line.

const date = todayDate(); //console-log: 2023-11-4
const timesasaddastamp = Date.parse(date);
const businessDayDate = new Date(timesasaddastamp); // console-log: Sat Nov 04 2023

How to shorten this?
I tried doing:

const businessDayDate = new Date.parse(date);

Mixing ImageCapture and non-ImageCapture constraints is not currently supported

I have started learning webRtc. There I found getUserMedia(constraints) to display media(audio/video). So I decided to dig deep the constraints. So there is a function getCapabilities() which return all supported constraints for a track. So I decided to create dynamic control(audio/video) separately like below. And on change of each controls I am calling applyConstraints(updatedConstraint) to apply the constraints.
enter image description here

Things are working fine. But There is one issue. If I change the brightness first then I am not able to change aspectRatio/height/width or If I change height/width first then I am not able to change brightness/contrast. I am getting an error.
Uncaught (in promise) OverconstrainedError: Mixing ImageCapture and non-ImageCapture constraints is not currently supported

Later I found that for video track we have 2 types constraints. One is for the Image and second is for the Video track. So My question is

  1. for video track how can we separate constraints for Image and Video dynamically. Because getCapabilities() does not return any flag.
  2. Even If we separate those constraints then how can we avoid this error Mixing ImageCapture and non-ImageCapture constraints is not currently supported

track.getCapabilities() return data like below for video track
enter image description here

If required then I can paste my code as well.

Given this scenario, do I still need to store the id of the station(table1) on the types(table2)?

I am using NextJS 14 and Supabase.

In my case, one account = one station. The user cannot add data on the types table before they have their station information filled up. Currently, this is my data definition where I store the user_id on both the station table and the types table. I would like to ask which approach is better:

  1. The user_id on both of the tables is already enough.
  2. Should I store the id of the station as an FK on the types table?

Data definition:

create table
  public.types (
    id uuid not null default uuid_generate_v4 (),
    name text not null,
    price numeric(10, 2) not null,
    user_id uuid not null,
    constraint types_pkey primary key (id),
    constraint types_user_id_fkey foreign key (user_id) references auth.users (id) on update cascade on delete restrict
  ) tablespace pg_default;

create table
  public.station (
    id uuid not null default gen_random_uuid (),
    created_at timestamp with time zone null,
    user_id uuid not null default auth.uid (),
    station_name text not null,
    address text not null,
    barangay text not null,
    remarks text null,
    contact_no numeric null,
    tel_no numeric null,
    delivery_mode text null,
    landmark text null,
    constraint station_pkey primary key (id),
    constraint station_user_id_key unique (user_id),
    constraint station_user_id_fkey foreign key (user_id) references auth.users (id) on update cascade on delete cascade
  ) tablespace pg_default;

I will also need these on a page where I will retrieve the corresponding types of that station. For example, for station 1, the types they have are type1, type2, and type3. And I would need to display it on the screen.

This will be used in an order page as well. The user can select the station they want. And from there, they can order the different types that the station has.

Issue with compilation in Android project: ‘Could not create task’ error – React Native DeepAR

I am currently working on an Android project using DeepAR and React Native. I’m encountering an error while trying to compile my project. The error message I’m getting is:

DeepAR: Execution failed for task ':tasks'. > Could not create task ':react-native-deepar:compileDebugJavaWithJavac'. > In order to compile Java 9+ source, please set compileSdkVersion to 30 or above.

I have already set compileSdkVersion to 33 in my project, and I couldn’t find any issues related to this specific problem. Here are the relevant parts of my build configuration:

buildscript {
ext {
    buildToolsVersion = "33.0.0"
    minSdkVersion = 21
    compileSdkVersion = 33
    targetSdkVersion = 33

    ndkVersion = "26.1.10909125"
}}

I’ve checked the documentation and forums but haven’t been able to resolve this. Can anyone point me in the right direction or provide insights on what might be causing this error? Any help would be greatly appreciated.

Thank you!

JQuery problem turning HTML element ‘visibility’ property on and off

Am trying to create a button that when clicked, shows the table with id ‘compose1’ and when the button is clicked again, the table collapses and is invisible.

The code works fine on first click, and shows the table, but won’t work on second click. The table won’t collapse back on second click.

I know the issue is somewhere at the code

var msgAttr= $(`#compose${msgid}`).style.visibility

because it gives me the error Uncaught TypeError: Cannot read properties of undefined (reading 'visibility').

Could you help me spot where the issue is?

Thanks in advance!

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<a style="text-decoration: none;" href="#" onclick="composeMsg('1'); return false;" title="Button"> Click Button </a>


<table width="100%" height="100%" border="0" id="compose1" style="visibility: collapse;">
<tr>
<td align="bottom" valign="bottom">
My Table
</td>
</tr>
</table>

<script>
    function composeMsg(msgid) {  

        var msgAttr= $(`#compose${msgid}`).style.visibility; 
        //var msgAttr = 'hidden';
        
        if(msgAttr=='hidden'){
          $(`#compose${msgid}`).attr("style","visibility: visible;"); 
        } else {
          $(`#compose${msgid}`).attr("style","visibility: collapse;"); 
        }

    }  
</script>

How to use observablehq to plot N line charts having shared zooming and tooltip?

I want to mimick the functionalities of Grafana due to its limitation in ObservableHQ.

I have simulated timeseries of 100 sensors and the x starts from 0 to 300s with steps of 1. Hence, all sensors have common x. Something like below,

x     sen1 sen2 …… sen100
————————————————————
0     10.1 8.3     11.7
1     10.7 8.9     11.2
2     10.5 8.2     11.6
3     11.0 9.2     11.7
……
300   10.8 8.7     11.1

Basically I want to give a dashboard to my users where there will be a drop down with sensor names. User can select more than one sensors. On selection, the dashboard should render line charts per sensor and approximately 3 sensors per row.

Something like ObservableHQ notebook.

I want shared zooming so I can zoom on one chart and all chart zooms with it. Likewise, I want shared tooltip so I hover on one chart and I can see all charts value at that x. Observablehq does not support this yet and I am new to Observablehq or D3.js.

Can one help on achieving shared zooming and tooltip in Observablehq?

Note 1: I also want axis to be shown and the charts filling horizontal space as much as possible.

Note 2: I saw this simple timeseries but not sure how to modified this to get shared tooltip and 3 per row.