I am trying to write a script:
const
// Define the output file
OUTPUT_FILE = 'd:\topics.txt';
var
// Current topic ID
aTopicId: string;
// List of output
aList: TStringList;
begin
// Init list
aList := TStringList.Create;
aList.Add('Topic Caption | Help ID | Help Context | Meta Description');
try
// Get first topic
aTopicId := HndTopics.GetTopicFirst();
// Loop through all topics
while aTopicId <> '' do
begin
// Add the topic to the list
aList.Add(Format('%s | %s | %d | %s', [
HndTopics.GetTopicCaption(aTopicId),
HndTopics.GetTopicHelpId(aTopicId),
HndTopics.GetTopicHelpContext(aTopicId),
HndTopics.GetTopicDescription(aTopicId)
]));
// Get next topic
aTopicId := HndTopics.GetTopicNext(aTopicId);
end;
// Create the file
aList.SaveToFile(OUTPUT_FILE);
finally
aList.Free;
end;
end.
When I build it in HelpNDoc:
Why is it saying:
[Error] script(9, 3): Unknown name "aList"
The authors of HelpNDoc provided some clarification about this issue. Starting with HelpNDoc 7, you need to add the var keyword each time you change the type: Migrating scripts from V6 to V7.
So my variable declaration section needed to be changed to something like:
var
// Current topic ID
aTopicId: string;
var
// List of output
aList: TStringList;
This is a limitation of HelpNDoc's scripting engine.
Related
During the installation of our PowerPoint add-in using Inno Setup installer, I need to get the currently used version of PowerPoint by querying an Application.PowerPoint object itself – instead of relying on registry entries which can't be guaranteed to give the correct value.
I have successfully implemented this for an MSI installer written with WIX based on this answer using this code:
Imports Microsoft.Office.Interop.PowerPoint
Public Class Environment
Public Shared Function GetPowerPointVersion() As String
Dim CurVer As String
Dim thisPowerPoint As Object
thisPowerPoint = New Application()
CurVer = thisPowerPoint.Version
thisPowerPoint.Quit()
Return CurVer
End Function
End Class
I don't entirely trust this to work in all situations (maybe paranoid), so will put in try/catch blocks and use the registry method if this fails.
I haven't been able to work out how to do a similar thing with Inno Setup installer. There are some examples of using DLLs – https://jrsoftware.org/ishelp/index.php?topic=scriptdll – but I can't see how I could create a function callable from Inno Setup from this which would return the version number.
You can use CreateOleObject to call PowerPoint and return the version:
[Code]
function GetPowerPointVersion(): string;
var
MyPowerPoint: Variant;
begin
MyPowerPoint := CreateOleObject('PowerPoint.Application');
Result := MyPowerPoint.Version;
MyPowerPoint.Quit;
end;
For completeness, this is the Pascal Script code which I am now using to get the PowerPoint version – based on Matej's answer, with a fallback to checking the registry if that fails:
function PowerPointVersion(): String;
var
key: String;
versionToUse: String;
installedPowerPoint: Variant;
begin
versionToUse := '';
try
installedPowerPoint := CreateOleObject('PowerPoint.Application');
versionToUse := installedPowerPoint.Version;
installedPowerPoint.Quit;
except
versionToUse := '';
end;
if versionToUse = '' then
begin
if RegQueryStringValue(GetHKLM, 'SOFTWARE\Microsoft\Office\ClickToRun\Configuration','VersionToReport', key) then
begin
versionToUse := key;
Delete(versionToUse, Pos('.', key), Length(key));
versionToUse := versionToUse + '.0';
end;
end;
if versionToUse = '' then
begin
if RegQueryStringValue(HKCR, 'PowerPoint.Application\CurVer\','', key) then
begin
StringChangeEx(key, 'PowerPoint.Application.', '', True);
versionToUse := key;
versionToUse := versionToUse + '.0';
end;
end;
try
// Check to see if versionToUse string can convert to a float:
StrToFloat(versionToUse);
Result := versionToUse;
except
Result := '';
end;
end;
It turns out that using Microsoft.Office.Interop.PowerPoint which is a NuGet package is not a good idea as it is not supported and will be prone to failure. See this discussion.
This external C# code will work and can be set up to be called from Inno Setup. However, using CreateOleObject within Inno Setup Pascal code as described in the accepted answer is far simpler.
[SupportedOSPlatform("windows")]
public class PowerPointEnvironment
{
public static string GetPowerPointVersion()
{
string CurVer = "";
Type? PowerPointType = Type.GetTypeFromProgID("PowerPoint.Application");
if (PowerPointType != null)
{
dynamic? thisPowerPoint = Activator.CreateInstance(PowerPointType);
if (thisPowerPoint != null)
{
CurVer = thisPowerPoint.version();
}
}
return CurVer;
}
}
I have following pl/sql block:
DECLARE
user_name varchar(255);
custom_exception EXCEPTION;
PRAGMA exception_init( custom_exception, -20001);
BEGIN
SELECT name
INTO user_name
FROM data_table where ID = '1';
IF user_name = 'temp' THEN
RAISE custom_exception;
END IF;
END;
When I run it from Oracle SQL Developer, it works fine.
But I am getting ORA-01036: illegal variable name/number while running it from nodejs.
Call from Nodejs code:
var output = await connection.execute(fs.readFileSync('path_to_pl_sql_file', 'utf8'), {
message: {
dir: oracledb.BIND_OUT,
type: oracledb.STRING,
maxSize: 100
}
}, {
autoCommit: true
});
Can someone point out what going wrong here?
The error was not with the PL/SQL block.
I was passing in bind variables while executing the PL/SQL from nodejs and that bind variable was not present in the SQL.
Hence the error!
I need to implement in PostgreSQL a table for each combination of option and variation.
This is my existing tables:
Imagine this scenario:
Product: T-Shirt
Options: Size, Color
Variations: Size:Small,Medium,Large | Color:White,Black,Yellow
I need to have a row for each combination to control quantity and price seperately.
So i need a row with Size:Small,Color:White, one with Size:Small,Color:Black etc...
It isn't necessary to execute all in SQL, i manipulate the data in program logic in Go.
I thought about making a table with:
id |price | quantity | option1_id | option2_id | option3_id
and compare the values that i get from my form and get the appropriate price and qty.However i do not know how to guarantee that the options on my form are displayed on the correct order as to not put the option id on my query.
Is there better option?Perhaps saving the combination in JSON on one column and loading in a array in JS and manipulating it there?
To explain it a bit better(hopefully):
I display on my site some select inputs with the options and variations that i have inserted,like this.
Where each option on the dropdown menu has a value of variation_id and the name of the menu is the option_id,ex. 120:1400 where 120 is the option_id and 1400 is the variant attached to it and currently selected.So when user adds product to cart it sends this JSON to server[{"option_id":3,"variant_id":13},{"option_id":433,"variant_id":1085}]
I know how to make a table with each possible combination, but i don't know how to link the variants selected to the one row on my table.
I want to implement the same way that Woocommerce does it.
This is the Attributes(Options)
And those are the Variations that are generated
As you can see there's an entry for each possible configuration.
Currently i have set up the Name and Values of the third image. product_option has the name of the option and there are multiple product_variation rows pointed to the option_id, each with a variation name like Small,Medium etc...
I don't know how to create a new table like the 4th image.
I have found a way.It's called Cartesian Product.
Here is how it's done.
This is a function that i found online,from this repo https://github.com/schwarmco/go-cartesian-product
func Iter(params ...[]interface{}) chan []interface{} {
// create channel
c := make(chan []interface{})
// create waitgroup
var wg sync.WaitGroup
// call iterator
wg.Add(1)
iterate(&wg, c, []interface{}{}, params...)
// call channel-closing go-func
go func() { wg.Wait(); close(c) }()
// return channel
return c
}
// private, recursive Iteration-Function
func iterate(wg *sync.WaitGroup, channel chan []interface{}, result []interface{}, params ...[]interface{}) {
// dec WaitGroup when finished
defer wg.Done()
// no more params left?
if len(params) == 0 {
// send result to channel
channel <- result
return
}
// shift first param
p, params := params[0], params[1:]
// iterate over it
for i := 0; i < len(p); i++ {
// inc WaitGroup
wg.Add(1)
// create copy of result
resultCopy := append([]interface{}{}, result...)
// call self with remaining params
go iterate(wg, channel, append(resultCopy, p[i]), params...)
}
}
Here's how you can use it
a := []interface{}{"Small", "Medium", "Large"}
b := []interface{}{"White", "Black", "Yellow"}
var d [][]interface{}
d = append(d,a)
d = append(d,b)
c := Iter(d...)
for product := range c {
fmt.Println(product)
}
I then store each product as a JSON string to the database
I'm using a Groovy script in Mule ESB to get output parameters from Oracle stored procedure (including cursor) and getting an exception.
Minimal example:
import groovy.sql.Sql
import oracle.jdbc.pool.OracleDataSource
import oracle.jdbc.driver.OracleTypes
def ds = new OracleDataSource()
// setting data source parameters here
def sql = new Sql(ds)
def data = []
sql.call("""declare
result_table sys_refcursor;
begin
open result_table for select 1 as a from dual;
insert into CURSOR_TEST (ID) values (1);
commit;
${Sql.resultSet OracleTypes.CURSOR} := result_table;
insert into CURSOR_TEST (ID) values (2);
commit;
end;
"""
){ table ->
throw new RuntimeException("Never getting this exception.")
table.eachRow {
data << it.toRowResult()
}
}
sql.close()
return data
Error:
Message : java.sql.SQLException: Closed Statement (javax.script.ScriptException)
Code : MULE_ERROR--2
--------------------------------------------------------------------------------
Exception stack is:
1. Closed Statement(SQL Code: 17009, SQL State: + 99999) (java.sql.SQLException)
oracle.jdbc.driver.SQLStateMapping:70 (null)
2. java.sql.SQLException: Closed Statement (javax.script.ScriptException)
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl:323 (http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/script/ScriptException.html)
3. java.sql.SQLException: Closed Statement (javax.script.ScriptException)
(org.mule.api.transformer.TransformerException)
org.mule.module.scripting.transformer.ScriptTransformer:39 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/transformer/TransformerException.html)
--------------------------------------------------------------------------------
Root Exception stack trace:
java.sql.SQLException: Closed Statement
at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:70)
at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:133)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:199)
+ 3 more (set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
********************************************************************************
Select from CURSOR_TEST returns 1 and 2.
Oracle server version: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production.
Mule version: 3.5.0.
I'm using jdbc\lib\ojdbc6.jar from oracle client version 11.1.0.7.0.
What am I doing wrong?
The following code can help you get variable of SYS_REFCURSOR from Oracle anonymous block.
We should focus on a few key details:
Class groovy.sql.Sql doesn't have corresponding OutParameter and we make it manually as CURSOR_PARAMETER and pass it to sql.call method
Consider that the block starts with {call DECLARE and ends with END } without semicolon after END. Otherwise we can get a poorly recognizable SQLException in the face.
The question marks ? inside the sqlString are places for parameter bindings. Bindings are made in the natural order. In this example:
the first ? binds with the first element in parametersList: "abc", treating the value as IN parameter ;
the second ? binds with CURSOR_PARAMETER treating the value as OUT parameter of passed type;
There is only one enter into closure after sql.call and ResultSet rs provide rows of cursor my_cur declared in anonymous block.
import groovy.sql.OutParameter
import groovy.sql.Sql
import oracle.jdbc.OracleTypes
import java.sql.ResultSet
def driver = 'oracle.jdbc.driver.OracleDriver'
def sql = Sql.newInstance('jdbc:oracle:thin:#MY-SERVER:1521:XXX', 'usr', 'psw', driver)
// special OutParameter for cursor type
OutParameter CURSOR_PARAMETER = new OutParameter() {
public int getType() {
return OracleTypes.CURSOR;
}
};
// look at some ceremonial wrappers around anonymous block
String sqlString = """{call
DECLARE
my_cur SYS_REFCURSOR;
x VARCHAR2(32767) := ?;
BEGIN
OPEN my_cur
FOR
SELECT x || level AS my_column FROM dual CONNECT BY level < 10;
? := my_cur;
END
}
""";
// the order of elements matches the order of bindings
def parametersList = ["abc", CURSOR_PARAMETER];
// rs contains the result set of cursor my_cur
sql.call(sqlString, parametersList) { ResultSet rs ->
while (rs.next()) {
println rs.getString("my_column")
}
};
I have a JSON Object, let's name it jObject that looks like this:
{
"id": 0,
"data": "[{DAT_INCL: \"08/03/2012 10:07:08\", NUM_ORDE: 1, NUM_ATND: 1, NUM_ACAO: 2, NUM_RESU: 3},
{DAT_INCL: \"08/03/2012 10:07:09\", NUM_ORDE: 2, NUM_ATND: 1, NUM_ACAO: 4, NUM_RESU: 5},
{DAT_INCL: \"08/03/2012 10:07:09\", NUM_ORDE: 3, NUM_ATND: 1, NUM_ACAO: 8, NUM_RESU: NULL}]"
}
As you can see, it contains two pairs, one of which is an array with three objects in this case (the amount of objects is dynamic) with multiple "key: values"(these don't vary, being always the same 5 fields), which I want to insert into an SQL database, "key" being column, "value" being field. Question is, how do I access each object individually?
Code-wise what I did was extract the pair that contained this array by putting it in jPair
jPair := OriginalObject.Get(1);
and then captured the array
jArray:= TJSONArray(jPair.JsonValue);
(Also, as a bonus, when I evaluate jArray.Size, the result is 6226004. What?)
If you have an array from DBXJSON, then it is a TJSONArray. Call its Get method to get an element of the array.
var
Value: TJSONValue;
Value := jArray.Get(0);
You can also go through the entire array with a for loop:
for Value in jArray do
But if you check the Size property and get 6226004 instead of 3, that suggests there's something else wrong here. My guess is that what you think is a TJSONArray isn't really that type. Use as to do a checked type cast:
jArray := jPair.JsonValue as TJSONArray;
You'll get an EInvalidCast exception if that fails.
here is an sample code to parse and output your json data. I've modified your JSON data and added ArrayData field, wich contains your initial array of objects:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, dbxjson;
const JSON_DATA = '{"ArrayData":['+
'{"DAT_INCL":"07/03/2012 17:33:03", "NUM_ORDE":1,"NUM_ATND":1, "NUM_ACAO":2, "NUM_RESU":3},'+
'{"DAT_INCL":"07/03/2012 17:33:05", "NUM_ORDE":2,"NUM_ATND":1, "NUM_ACAO":4, "NUM_RESU":5},'+
'{"DAT_INCL":"07/03/2012 17:33:05", "NUM_ORDE":3,"NUM_ATND":1, "NUM_ACAO":8, "NUM_RESU":null}'+
']}';
var jsv : TJsonValue;
originalObject : TJsonObject;
jsPair : TJsonPair;
jsArr : TJsonArray;
jso : TJsonObject;
i : integer;
begin
try
//parse json string
jsv := TJSONObject.ParseJSONValue(JSON_DATA);
try
//value as object
originalObject := jsv as TJsonObject;
//get pair, wich contains Array of objects
jspair := originalObject.Get('ArrayData');
//pair value as array
jsArr := jsPair.jsonValue as TJsonArray;
writeln('array size: ', jsArr.Size);
//enumerate objects in array
for i := 0 to jsArr.Size - 1 do begin
writeln('element ', i);
// i-th object
jso := jsArr.Get(i) as TJsonObject;
//enumerate object fields
for jsPair in jso do begin
writeln(' ', jsPair.JsonString.Value, ': ', jsPair.JsonValue.Value);
end;
end;
finally
jsv.Free();
readln;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Practical example of adding JSON data from an array to a TComboBox
This example assumes you have included the JSON unit. It also assumes you have a TComboBox named cmbCompany. There is nothing wrong with the accepted answers, I just wanted to document.
uses JSON;
procedure LoadCompanies;
var
i : Integer;
companyArray : TJsonArray;
company : TJsonObject;
begin
//get the company data
companyData := TJSONObject.ParseJSONValue('{data:[{"name": "One"},{"name": "Two"}]}') as TJSONObject;
try
cmbCompany.Items.Clear;
with companyData do
begin
//Important bit which relates to the question!
companyArray := (Get('data').JsonValue as TJSONArray);
try
for i := 0 to companyArray.Size-1 do
begin
//Get one of the array values
company := (companyArray.Get(i) as TJSONObject);
//Add name to combo box, you can obviously get other values in similar fashion
cmbCompany.Items.Add(company.GetValue<String>('name'));
end;
finally
companyArray.Free;
end;
end;
finally
companyData.Free;
end;
end;