File Coverage

blib/lib/HTTP/QuickBase.pm
Criterion Covered Total %
statement 69 825 8.3
branch 14 364 3.8
condition 3 28 10.7
subroutine 10 73 13.7
pod 32 70 45.7
total 128 1360 9.4


line stmt bran cond sub pod time code
1             package HTTP::QuickBase;
2              
3             #Version $Id: QuickBase.pm,v 1.55 2013/08/09 15:29:23 cvonroes Exp $
4              
5             ( $VERSION ) = '$Revision: 1.55 $ ' =~ /\$Revision:\s+([^\s]+)/;
6              
7 1     1   725 use strict;
  1         1  
  1         32  
8 1     1   2726 use LWP::UserAgent;
  1         73643  
  1         36  
9 1     1   907 use MIME::Base64 qw(encode_base64);
  1         797  
  1         11635  
10              
11             =pod
12              
13             =head1 NAME
14              
15             HTTP::QuickBase - Create a web shareable database in under a minute
16              
17             =head1 VERSION
18              
19             $Revision: 1.54 $
20              
21             =head1 SYNOPSIS
22              
23             # see https://www.quickbase.com/up/6mztyxu8/g/rc7/en/ for details of the underlying API.
24            
25             use HTTP::QuickBase;
26             $qdb = HTTP::QuickBase->new();
27            
28             #If you don't want to use HTTPS or your Perl installation doesn't support HTTPS then
29             #make sure you have the "Allow non-SSL access (normally OFF)" checkbox checked on your
30             #QuickBase database info page. You can get to this page by going to the database "MAIN"
31             #page and then clicking on "Administration" under "SHORTCUTS". Then click on "Basic Properties".
32             #To use this module in non-SSL mode invoke the QuickBase object like this:
33            
34             #$qdb = HTTP::QuickBase->new('http://www.quickbase.com/db');
35              
36             $username="fred";
37             $password="flinstone";
38              
39             $qdb->authenticate($username, $password);
40             $database_name= "GuestBook Template";
41            
42             #I don't recommend using the getIDbyName method because there are many tables with the same name.
43             #Instead you can discover the database_id of your table empirically.
44             #Read the follwing article to find out how:
45             #https://www.quickbase.com/db/6mztyxu8?a=dr&r=w
46            
47             $database_id = "9mztyxu8";
48             $clone_name = "My Guest Book";
49             $database_clone_id = $qdb->cloneDatabase($database_id, $clone_name, "Description of my new database.");
50              
51              
52             #Let's put something into the new guest book
53             $Name = "Fred Flinstone";
54             $dphone = "978-533-2189";
55             $ephone = "781-839-1555";
56             $email = "fred\@bedrock.com";
57             $address1 = "Rubble Court";
58             $address2 = "Pre Historic Route 1";
59             $city = "Bedrock";
60             $state = "Stonia";
61             $zip = "99999-1234";
62             $comments = "Hanna Barbara the king of Saturday morning cartoons.";
63             #if you want to attach a file you need to create an array with the first member of the array set to the literal string "file" and the second
64             #member of the array set to the full path of the file.
65             $attached_file = ["file", "c:\\my documents\\bedrock.txt"];
66             %record_data=("Name" => $Name,"Daytime Phone" => $dphone, "Evening Phone" =>$ephone,"Email Address" => $email, "Street Address 1" => $address1,"Street Address 2" => $address2,"City" => $city,"State"=>$state,"Zip Code"=>$zip, "Comments" => $comments , "Attached File" => $attached_file );
67              
68             $record_id = $qdb->AddRecord($database_clone_id, %record_data);
69              
70             #Let's get that information back out again
71             %new_record=$qdb->GetRecord($database_clone_id, $record_id);
72             #Now let's edit that record!
73             $new_record{"Daytime Phone"} = "978-275-2189";
74             $qdb->EditRecord($database_clone_id, $record_id, %new_record);
75            
76             #Let's print out all records in the database.
77              
78             @records = $qdb->doQuery($database_clone_id, "{0.CT.''}");
79             foreach $record (@records){
80             foreach $field (keys %$record){
81             print "$field -> $record->{$field}\n";
82             }
83             }
84              
85             #Let's save the entire database to a local comma separated values (CSV) file.
86            
87             open( CSV, ">my_qbd_snapshot.csv");
88             print CSV $qdb->getCompleteCSV($database_clone_id);
89             close CSV;
90            
91             #Where field number 10 contains Wilma (the query)
92             #let's print out fields 10, 11, 12 and 15 (the clist)
93             #sorted by field 14 (the slist)
94             #in descending order (the options)
95              
96             @records = $qdb->doQuery($database_clone_id, "{10.CT.'Wilma'}", "10.11.12.15", "14", "sortorder-D");
97             foreach $record (@records){
98             foreach $field (keys %$record){
99             print "$field -> $record->{$field}\n";
100             }
101             }
102              
103             #You can find out what you need in terms of the query, clist, slist and options by
104             #going to the View design page of your QuickBase database and filling in the form. Hit the "Display" button and
105             #look at the URL in the browser "Address" window. The View design page is accessible from any database home
106             #page by clicking on VIEWS at the top left and then clicking on "New View..." in the lower left.
107              
108             =head1 REQUIRES
109              
110             Perl5.005, LWP::UserAgent, Crypt::SSLeay (optional unless you want to talk to QuickBase via HTTPS)
111              
112             =head1 SEE ALSO
113              
114             https://www.quickbase.com/up/6mztyxu8/g/rc7/en/ for details of the underlying QuickBase HTTP API
115              
116             =head1 EXPORTS
117              
118             Nothing
119              
120             =head1 DESCRIPTION
121              
122             HTTP::QuickBase allows you to manipulate QuickBase databases.
123             Methods are provided for cloning databases, adding records, editing records, deleting records and retrieving records.
124             All you need is a valid QuickBase account, although with anonymous access you can read from publically accessible QuickBase
125             databases. To learn more about QuickBase please visit http://www.quickbase.com/
126             This module supports a single object that retains login state. You call the authenticate method only once.
127              
128             =head1 METHODS
129              
130             =head2 Creation
131              
132             =over 4
133              
134             =item $qdb = new HTTP::QuickBase($URLprefix)
135              
136             Creates and returns a
137             new HTTP::QuickBase object.
138             Use the optional $URLprefix to connect to QuickBase via HTTP instead of HTTPS.
139             call the constructor with a URLprefix parameter of "http://www.quickbase.com/db/".
140             QuickBase databases are by default not accessible via HTTP. To allow HTTP access to a
141             QuickBase database go to its main page and click on "Administration" under "SHORTCUTS".
142             Then click on "Basic Properties". Next to "Options" you'll see a checkbox labeled
143             "Allow non-SSL access (normally unchecked)". You'll need to check this box to allow HTTP
144             access to the database.
145              
146             =back
147              
148             =head2 Authentication/Permissions
149              
150             =over 4
151              
152             =item $qdb->authenticate($username, $password)
153              
154             Sets the username and password used for subsequent method invocations
155              
156             =back
157              
158             =head2 Finding IDs
159              
160             =over 4
161              
162             =item $qdb->getIDbyName($dbName)
163              
164             Returns the database ID of the database whose full name matches $dbName.
165             I don't recommend using the getIDbyName method because there are many tables with the same name.
166             Instead you can discover the database_id of your table empirically.
167             Read the follwing article to find out how:
168             https://www.quickbase.com/db/6mztyxu8?a=dr&r=w
169              
170             =item $qdb->GetRIDs ($QuickBaseID)
171              
172             Returns an array of all record IDs in the database identified by database ID $QuickBaseID.
173              
174             =back
175              
176             =head2 Cloning and Creating from Scratch
177              
178             =over 4
179              
180              
181             =item $qdb->cloneDatabase($QuickBaseID, $Name, $Description)
182              
183             Clones the database identified by $QuickBaseID and gives the clone the name $Name and description $Description
184              
185             Returns the dbid of the new database.
186              
187             =back
188              
189             =over 4
190              
191             =item $qdb->createDatabase($Name, $Description)
192              
193             Creates a database with the name $Name and description $Description
194              
195             Returns the dbid of the new database.
196              
197             =back
198              
199             =over 4
200              
201             =item $qdb->addField($QuickBaseID, $label, $type, $mode)
202              
203             Creates a field with the label $label of label, a type of $type and if the field is to be a formula field then set $mode to 'virtual' otherwise set it to the empty string.
204              
205             Returns the fid of the new field.
206              
207             =back
208              
209             =over 4
210              
211             =item $qdb->deleteField($QuickBaseID, $fid)
212              
213             Deletes the field with the field identifier of $fid.
214              
215             Returns nothing.
216              
217             =back
218              
219             =over 4
220              
221             =item $qdb->setFieldProperties($QuickBaseID, $fid, %properties)
222              
223             Modifies the field with the field identifier of $fid using the name-value pairs in %properties. Please see the QuickBase HTTP API document for more details.
224              
225             Returns nothing.
226              
227             =back
228              
229              
230             =head2 Adding Information
231              
232             =over 4
233              
234              
235             =item $qdb->AddRecord($QuickBaseID, %recorddata)
236              
237             Returns the record id of the new record. The keys of the associative array %recorddata are scanned for matches with the
238             field names of the database. If the key begins with the number one through nine and contains only numbers
239             then the field identifiers are scanned for a match instead.
240             If a particular key matches then the corresponding field in the new record is set to the value associated with the key.
241             If you want to attach a file you need to create an array with the first member of the array set to the string literal 'file' and the second
242             member of the array set to the full path of the file. Then the value of the key corresponding to the file attachment field
243             should be set to a reference which points to this two member array.
244              
245             =back
246              
247             =head2 Deleting Information
248              
249             =over 4
250              
251             =item $qdb->DeleteRecord($QuickBaseID, $rid)
252              
253             Deletes the record identified by the record identifier $rid.
254              
255             =back
256              
257             =over 4
258              
259             =item $qdb->PurgeRecords($QuickBaseID, $query)
260              
261             Deletes the records identified by the query, qname or qid in $query. Use the qid of '1' to delete all the records in a database.
262              
263             Please refer to https://www.quickbase.com/db/6mztyxu8?a=dr&r=2 for more details on the query parameter.
264              
265             =back
266              
267              
268              
269             =head2 Editing Information
270              
271             =over 4
272              
273             =item $qdb->EditRecord($QuickBaseID, $rid, %recorddata)
274              
275             Modifies the record defined by record id $rid in the database defined by database ID $QuickBaseID.
276              
277             Any field in the database that can be modified and that has its field label or field identifer as a key in the associative array
278             %recorddata will be modified to the value associated with the key. The keys of the associative array %recorddata are scanned for matches with the
279             field names of the database. If the key begins with the number one through nine and contains only numbers
280             then the field identifiers are scanned for a match instead.
281             If a particular key matches then the corresponding field in the record is set to the value associated with the key.
282             If you want to modify a file attachment field, you need to create an array with the first member of the array set to the string literal 'file' and the second
283             member of the array set to the full path of the file. Then the value of the key corresponding to the file attachment field
284             should be set to a reference which points to this two member array.
285              
286              
287             Use $qdb->EditRecordWithUpdateID($QuickBaseID, $rid, $update_id, %recorddata) to take advantage of conflict detection.
288             If $update_id is supplied then the edit will only succeed if the record's current update_id matches.
289              
290             Returns the XML response from QuickBase after modifying every valid field refered to in %recorddata.
291              
292             Not all fields can be modified. Built-in and formula (virtual) fields cannot be modified. If you attempt to
293             modify them with EditRecord you will get an error and no part of the record will have been modified.
294              
295             =back
296              
297             =head2 Retrieving Information
298              
299             =over 4
300              
301             =item $qdb->GetRecord($QuickBaseID, $rid)
302              
303             From the database identified by $QuickBaseID, returns an associative array of field names and values of the record identified by $rid.
304              
305             =back
306              
307             =over 4
308              
309             =item $qdb->doQuery($QuickBaseID, $query, $clist, $slist, $options)
310              
311             From the database identified by $QuickBaseID, returns an array of
312             associative arrays of field names and values of the records selected by
313             $query, which can either be an actual query in QuickBase's query
314             language, or a view name or number (qid or qname).
315              
316             The columns (fields) returned are determined by $clist, a period delimited list of field identifiers.
317              
318             The sorting of the records is determined by $slist, a period delimited list of field identifiers.
319              
320             Ascending or descending order of the sorts defined by $slist is controlled by $options.
321              
322             Please refer to https://www.quickbase.com/db/6mztyxu8?a=dr&r=2 for more details on the parameters for API_DoQuery.
323              
324             =back
325              
326             =over 4
327              
328             =item $qdb->getCompleteCSV($QuickBaseID)
329              
330             From the database identified by $QuickBaseID, returns a scalar containing the comma separated values of all fields including built in fields.
331              
332             The first row of the comma separated values (CSV) contains the field labels.
333              
334             =back
335              
336             =over 4
337              
338             =item $qdb->GetFile($QuickBaseDBid, $filename, $rid, $fid)
339              
340             From the database identified by $QuickBaseID, returns an array where the first element is the contents of the file $filename uploaded to
341             the record identified by record ID $rid in the field identified by field indentifier $fid.
342              
343             The second element of the returned array is return value from the headers method of the corresponding LWP::UserAgent object.
344              
345             =back
346              
347              
348              
349              
350             =head2 Errors
351              
352             =over 4
353              
354             =item $qdb->error()
355              
356             Retrieve the error code returned from QuickBase.
357             Please refer to the
358            
359             Appendix A for error code details.
360              
361             =item $qdb->errortext()
362              
363             Retrieve the error text returned from QuickBase.
364             Please refer to
365            
366             Appendix A for all possible error messages.
367              
368              
369             =back
370              
371              
372             =head2 New API calls added in 2008
373              
374              
375              
376              
377             =over 4
378              
379             =item CreateTable($QuickBaseDBid, $pnoun)
380              
381             Add a table to an existing application.
382              
383             Returns the dbid of the new table.
384              
385             =back
386              
387              
388              
389             =over 4
390              
391             =item AddUserToRole($QuickBaseDBid, $userid, $roleid)
392              
393             Add a user to a role in an application.
394              
395             =back
396              
397              
398              
399             =over 4
400              
401             =item ChangeUserRole($QuickBaseDBid, $userid, $roleid, $newroleid)
402              
403             Change the role of a user in an application.
404              
405             =back
406              
407              
408              
409             =over 4
410              
411             =item GetDBvar($QuickBaseDBid, $varname)
412              
413             Retrieve the value of an application variable.
414              
415             =back
416              
417             =over 4
418              
419             =item GetRoleInfo($QuickBaseDBid)
420              
421             Retrieve the list of Roles defined for an application.
422              
423             =back
424              
425             =over 4
426              
427             =item GetUserInfo($email)
428              
429             Retrieve a hash containing the login, name, and id of a user, given the user's email address.
430              
431             =back
432              
433              
434             =over 4
435              
436             =item GetUserRole($QuickBaseDBid,$userid)
437              
438             Retrieve the Role information for a user
439              
440             =back
441              
442             =over 4
443              
444             =item ProvisionUser($QuickBaseDBid,$roleid, $email, $fname, $lname)
445              
446             Add the user information to QuickBase in preparation for inviting the user for the first time to view a QuickBase application.
447              
448             =back
449              
450              
451             =over 4
452              
453             =item GetOneTimeTicket
454              
455             Retrieve a ticket valid for the next 5 minutes only. Designed for uploading files.
456              
457             =back
458              
459             =over 4
460              
461             =item RemoveUserFromRole($QuickBaseDBid, $userid, $roleid)
462              
463             Remove a user from a role in an application.
464              
465             =back
466              
467              
468             =over 4
469              
470             =item RenameApp($QuickBaseDBid,$newappname)
471              
472             Change the name of an application.
473              
474             =back
475              
476              
477             =over 4
478              
479             =item SetDBvar($QuickBaseDBid, $varname, $value)
480              
481             Set the value of an application variable.
482              
483             =back
484              
485              
486             =over 4
487              
488             =item SendInvitation($QuickBaseDBid, $userid)
489              
490             Send an email from QuickBase inviting a user to an application.
491              
492             =back
493              
494              
495             =over 4
496              
497             =item UserRoles($QuickBaseDBid)
498              
499             Returns an Xml Document of information about the roles defined for an application.
500              
501             =back
502              
503              
504              
505             =head1 CLASS VARIABLES
506              
507             None
508              
509             =head1 DIAGNOSTICS
510              
511             All errors are reported by the methods error and errortext. For a
512             complete list of errors, please visit
513             https://www.quickbase.com/up/6mztyxu8/g/rc7/en/ and scroll
514             down to Appendix A.
515              
516             =head1 AUTHOR
517              
518             Claude von Roesgen, claude_von_roesgen@intuit.com
519              
520             =head1 COPYRIGHT
521              
522             Copyright (c) 1999-2008 Intuit, Inc. All rights reserved.
523             This program is free software; you can redistribute it and/or
524             modify it under the same terms as Perl itself.
525              
526              
527             =cut
528              
529             my %XMLescapes;
530              
531             sub new
532             {
533 1     1 1 14 my $class = shift;
534 1         2 my $prefix = shift;
535 1         2 my $self;
536              
537 1         4 for (0..255) {
538 256         938 $XMLescapes{chr($_)} = sprintf("&#%03d;", $_);
539             }
540              
541 1   50     21 $self = bless {
542             'URLprefix' => $prefix || "https://www.quickbase.com/db" ,
543             'ticket' => undef,
544             'apptoken' => "",
545             'error' => undef,
546             'errortext' => undef,
547             'username' => undef,
548             'password' => undef,
549             'credentials' => undef,
550             'proxy' => undef,
551             'realmhost' => undef
552             }, $class;
553              
554             }
555              
556             sub authenticate ($$)
557             {
558 1     1 1 13 my($self, $username, $password) = @_;
559 1         7 $self->{'username'} = $username;
560 1         4 $self->{'password'} = $password;
561 1         6 $username = $self->xml_escape($username);
562 1         11 $password = $self->xml_escape($password);
563 1         5 $self->{'credentials'} = "$username<\/username>$password<\/password>";
564 1         3 $self->{'ticket'}="";
565 1         2 return "";
566             }
567              
568             sub setAppToken($)
569             {
570 0     0 0 0 my($self,$apptoken) = @_;
571 0         0 $self->{'apptoken'} = $apptoken;
572             }
573              
574             sub getTicket()
575             {
576 0     0 0 0 my($self) = @_;
577             #First we have to get the authorization ticket
578             #We do this by posting the QuickBase username and password to QuickBase
579             #This is where we post the QuickBase username and password
580 0         0 my $res = $self->PostAPIURL ("main", "API_Authenticate",
581             "".
582             $self->{'credentials'}.
583             "");
584 0 0       0 if ($res->content =~ /(.*?)<\/errcode>.*?(.*?)<\/errtext>/s)
585             {
586 0         0 $self->{'error'} = $1;
587 0         0 $self->{'errortext'} = $2;
588             }
589 0 0       0 if ($res->content =~ /(.*?)<\/errdetail>/s)
590             {
591 0         0 $self->{'errortext'} = $1;
592             }
593 0 0       0 if ($self->{'error'} eq '0')
594             {
595 0         0 $res->content =~ /(.*?)<\/ticket>/s;
596 0         0 $self->{'ticket'} = $1;
597 0         0 $self->{'credentials'} = "$self->{'ticket'}<\/ticket>";
598             }
599             else
600             {
601 0         0 return "";
602             }
603 0         0 return $self->{'ticket'};
604             }
605              
606             sub URLprefix()
607             {
608 1     1 0 3 my($self) = shift;
609 1 50       5 if (@_)
610             {
611 0         0 $self->{'URLprefix'}=shift;
612 0         0 $self->{'URLprefix'} =~ s/cgi\/sb.exe/db/;
613 0         0 return $self->{'URLprefix'};
614             }
615             else
616             {
617 1         10 return $self->{'URLprefix'};
618             }
619             }
620              
621             sub setProxy($)
622             {
623 0     0 0 0 my($self, $proxyserver) = @_;
624 0         0 $self->{'proxy'} = $proxyserver;
625 0         0 return $self->{'proxy'};
626             }
627              
628             sub setRealmHost($)
629             {
630 0     0 0 0 my($self, $realmhost) = @_;
631 0         0 $self->{'realmhost'} = $realmhost;
632 0         0 return $self->{'realmhost'};
633             }
634              
635             sub errortext()
636             {
637 1     1 1 37 my($self) = shift;
638 1         0 return $self->{'errortext'};
639             }
640              
641             sub error()
642             {
643 0     0 1 0 my($self) = shift;
644 0         0 return $self->{'error'};
645             }
646              
647             sub AddRecord($%)
648             {
649 0     0 1 0 my($self, $QuickBaseDBid, %recorddata) = @_;
650 0         0 my $name;
651             my $content;
652 0         0 my $filecontents;
653 0         0 my $filebuffer;
654 0         0 my $tag;
655              
656 0         0 $content = "";
657 0         0 foreach $name (keys(%recorddata))
658             {
659 0         0 $tag=$name;
660 0         0 $tag =~tr/A-Z/a-z/;
661 0         0 $tag=~s/[^a-z0-9]/_/g;
662 0         0 $content .= $self->createFieldXML($tag, $recorddata{$name});
663             }
664              
665 0         0 $content .= "";
666 0         0 my $res = $self->PostAPIURL ($QuickBaseDBid, "API_AddRecord", $content);
667 0         0 my $xml = $res->content;
668              
669 0 0       0 if ($xml =~ /(.*)<\/rid>/ )
670             {
671 0         0 return $1;
672             }
673 0         0 return "";
674             }
675              
676             sub AddReplaceDBPage($$$$$)
677             {
678 0     0 0 0 my($self,$QuickBaseDBid, $pageid, $pagename, $pagetype, $pagebody) = @_;
679            
680 0         0 my $content = "";
681 0 0       0 $content .= "$pageid" if $pageid ne "";
682 0 0       0 $content .= "$pagename" if $pagename ne "";
683 0         0 $content .= "$pagetype".$self->xml_escape($pagebody)."";
684            
685 0         0 my $res = $self->PostAPIURL ($QuickBaseDBid, "API_AddReplaceDBPage", $content)->content;
686            
687 0 0       0 if($res =~ /(.*)<\/pageid>/ ){
    0          
688 0         0 return $1;
689             }
690             elsif($res =~ /(.*)<\/pageID>/ ){
691 0         0 return $1;
692             }
693             else
694             {
695 0         0 return "";
696             }
697             }
698              
699             sub AddUserToRole($$$)
700             {
701 0     0 1 0 my($self,$QuickBaseDBid, $userid, $roleid) = @_;
702 0         0 my $content = "$userid$roleid";
703 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_AddUserToRole", $content);
704 0         0 return "";
705             }
706              
707             sub ChangeUserRole($$$$)
708             {
709 0     0 1 0 my($self,$QuickBaseDBid, $userid, $roleid, $newroleid) = @_;
710 0         0 my $content = "$userid$roleid";
711 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_AddUserToRole", $content);
712 0         0 return "";
713             }
714              
715             sub ChangeRecordOwner($$$)
716             {
717 0     0 0 0 my($self, $QuickBaseDBid, $rid, $newowner);
718            
719 0         0 my $content = "$rid$newowner";
720 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_ChangeRecordOwner", $content);
721 0         0 return"";
722             }
723              
724             sub CreateTable($$)
725             {
726 0     0 1 0 my($self,$QuickBaseDBid, $pnoun) = @_;
727 0         0 my $content = "".$self->xml_escape($pnoun)."";
728 0         0 my $res = $self->PostAPIURL ($QuickBaseDBid, "API_CreateTable", $content)->content;
729 0 0       0 if($res =~ /(.*)<\/newdbid>/ ){
    0          
730 0         0 return $1;
731             }
732             elsif($res =~ /(.*)<\/newDBID>/ ){
733 0         0 return $1;
734             }
735             else
736             {
737 0         0 return "";
738             }
739             }
740              
741             sub DeleteDatabase($)
742             {
743 0     0 0 0 my($self,$QuickBaseDBid) = @_;
744 0         0 $self->PostAPIURL($QuickBaseDBid, "API_DeleteDatabase", "");
745 0         0 return "";
746             }
747              
748             sub DeleteRecord($$)
749             {
750 0     0 1 0 my($self, $QuickBaseDBid, $rid) = @_;
751              
752 0         0 my $content = "".
753             " $rid".
754             "";
755 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_DeleteRecord", $content)->content;
756             }
757              
758             sub FieldAddChoices($$@)
759             {
760 0     0 0 0 my($self,$QuickBaseDBid, $fid, @choices) = @_;
761            
762 0         0 my $content = "$fid";
763 0         0 my $choice;
764 0         0 foreach $choice (@choices)
765             {
766 0         0 $content .= "$choice";
767             }
768 0         0 $content .= "";
769            
770 0         0 my $res = $self->PostAPIURL ($QuickBaseDBid, "API_FieldAddChoices", $content)->content;
771            
772 0 0       0 if($res =~ /(.*)<\/numadded>/ ){
773 0         0 return $1;
774             }
775             else
776             {
777 0         0 return "";
778             }
779             }
780              
781             sub FieldRemoveChoices($$@)
782             {
783 0     0 0 0 my($self,$QuickBaseDBid, $fid, @choices) = @_;
784            
785 0         0 my $content = "$fid";
786 0         0 my $choice;
787 0         0 foreach $choice (@choices)
788             {
789 0         0 $content .= "$choice";
790             }
791 0         0 $content .= "";
792            
793 0         0 my $res = $self->PostAPIURL ($QuickBaseDBid, "API_FieldRemoveChoices", $content)->content;
794            
795 0 0       0 if($res =~ /(.*)<\/numremoved>/ ){
796 0         0 return $1;
797             }
798             else
799             {
800 0         0 return "";
801             }
802             }
803              
804             sub GenAddRecordForm($%)
805             {
806 0     0 0 0 my($self,$QuickBaseDBid,%fields) = @_;
807 0         0 my $content = "";
808 0         0 my $field;
809 0         0 foreach $field (keys %fields)
810             {
811 0         0 $content .= "$fields{$field}";
812             }
813 0         0 $content .= "";
814 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_GenAddRecordForm", $content)->content;
815             }
816              
817             sub GenResultsTable($$$$$$$)
818             {
819 0     0 0 0 my($self, $QuickBaseDBid, $query, $clist, $slist, $jht, $jsa, $options) = @_;
820 0         0 my $content = "";
821 0 0       0 $content .= "$query" if $query ne "";
822 0 0       0 $content .= "$clist" if $clist ne "";
823 0 0       0 $content .= "$slist" if $slist ne "" ;
824 0 0       0 $content .= "$jht" if $jht ne "";
825 0 0       0 $content .= "$jsa" if $jsa ne "";
826 0 0       0 $content .= "$options" if $options ne "";
827 0         0 $content .= "";
828 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_GenAddRecordForm", $content)->content;
829             }
830              
831             sub GetDBInfo($)
832             {
833 0     0 0 0 my($self,$QuickBaseDBid) = @_;
834            
835 0         0 my $res = $self->PostAPIURL ($QuickBaseDBid, "API_GetDBInfo", "")->content;
836            
837 0         0 my %dbInfo;
838 0 0       0 if($res =~ /(.*)<\/dbname>/ ){
839 0         0 $dbInfo{"dbname"} = $1;
840             }
841 0 0       0 if($res =~ /(.*)<\/version>/ ){
842 0         0 $dbInfo{"version"} = $1;
843             }
844 0 0       0 if($res =~ /(.*)<\/lastRecModTime>/ ){
845 0         0 $dbInfo{"lastRecModTime"} = $1;
846             }
847 0 0       0 if($res =~ /(.*)<\/lastModifiedTime>/ ){
848 0         0 $dbInfo{"lastModifiedTime"} = $1;
849             }
850 0 0       0 if($res =~ /(.*)<\/createdTime>/ ){
851 0         0 $dbInfo{"createdTime"} = $1;
852             }
853 0 0       0 if($res =~ /(.*)<\/lastAccessTime>/ ){
854 0         0 $dbInfo{"lastAccessTime"} = $1;
855             }
856 0 0       0 if($res =~ /(.*)<\/numRecords>/ ){
857 0         0 $dbInfo{"numRecords"} = $1;
858             }
859 0 0       0 if($res =~ /(.*)<\/mgrID>/ ){
860 0         0 $dbInfo{"mgrID"} = $1;
861             }
862 0 0       0 if($res =~ /(.*)<\/mgrName>/ ){
863 0         0 $dbInfo{"mgrName"} = $1;
864             }
865 0         0 return %dbInfo;
866             }
867              
868             sub GetDBPage($$$)
869             {
870 0     0 0 0 my($self, $QuickBaseDBid, $pageid, $pagename) = @_;
871            
872 0         0 my $content = "";
873 0 0       0 $content .= "$pageid" if $pageid ne "";
874 0 0       0 $content .= "$pagename" if $pagename ne "";
875 0         0 $content .= "";
876            
877 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_GetDBPage", $content)->content;
878             }
879              
880             sub GetDBvar($$)
881             {
882 0     0 1 0 my($self,$QuickBaseDBid, $varname) = @_;
883 0         0 my $content = "$varname";
884 0         0 my $res = $self->PostAPIURL($QuickBaseDBid, "API_GetDBvar", $content)->content;
885 0 0       0 if($res =~ /(.*)<\/value>/ ){
886 0         0 return $1;
887             }
888             else
889             {
890 0         0 return "";
891             }
892             }
893              
894             sub GetNumRecords($)
895             {
896 0     0 0 0 my($self,$QuickBaseDBid) = @_;
897 0         0 my $res = $self->PostAPIURL ($QuickBaseDBid, "API_GetNumRecords", "")->content;
898 0 0       0 if($res =~ /(.*)<\/num_records>/ ){
899 0         0 return $1;
900             }
901             else
902             {
903 0         0 return "";
904             }
905             }
906              
907             sub GetOneTimeTicket()
908             {
909 0     0 1 0 my($self) = @_;
910 0         0 my $res = $self->PostAPIURL ("main", "API_GetOneTimeTicket", "")->content;
911 0 0       0 if($res =~ /(.*)<\/ticket>/ ){
912 0         0 return $1;
913             }
914             else
915             {
916 0         0 return "";
917             }
918             }
919              
920             sub GetRecord($$)
921             {
922 0     0 1 0 my($self, $QuickBaseDBid, $rid) = @_;
923 0         0 my $content;
924             my @record;
925 0         0 my %record;
926 0         0 my $true=1;
927 0         0 my $false=0;
928 0         0 my $isFieldname = $false;
929 0         0 my $isFieldvalue = $false;
930 0         0 my $isFieldprintable = $false;
931 0         0 my ($fieldname, $fieldvalue, $fieldprintable) = ("","","");
932              
933 0         0 $content = "".
934             " $rid".
935             "";
936 0         0 my $res = $self->PostAPIURL ($QuickBaseDBid, "API_GetRecordInfo", $content);
937 0         0 my $recordXML = $res->content;
938 0         0 $recordXML =~ s//\n/ig;
939 0         0 @record = $recordXML =~ /<([A-Z\-\.0-9]+)>([^<]*)<\/\1>/isg;
940 0         0 my $count = 0;
941 0         0 my $record;
942              
943 0         0 foreach $record(@record){
944 0 0       0 unless ($count % 2)
945             {
946 0 0       0 if($record=~/^name$/)
    0          
    0          
947             {
948 0         0 $isFieldname = $true;
949 0 0       0 if ($fieldname)
950             {
951 0         0 $fieldname = $self->xml_unescape($fieldname);
952 0 0       0 if($fieldprintable){
    0          
953 0         0 $record{$fieldname} = $self->xml_unescape($fieldprintable);
954             }elsif($fieldvalue){
955 0         0 $record{$fieldname} = $self->xml_unescape($fieldvalue);
956             }
957             }
958 0         0 $fieldname=""; $fieldvalue=""; $fieldprintable="";
  0         0  
  0         0  
959             }
960             elsif($record=~/^value$/)
961             {
962 0         0 $isFieldvalue = $true;
963             }
964             elsif($record=~/^printable$/)
965             {
966 0         0 $isFieldprintable = $true;
967             }
968             }
969             else
970             {
971 0 0       0 if($isFieldname)
    0          
    0          
972             {
973 0         0 $fieldname = $record;
974 0         0 $isFieldname = $false;
975             }
976             elsif($isFieldvalue)
977             {
978 0         0 $fieldvalue = $record;
979 0         0 $isFieldvalue = $false;
980             }
981             elsif($isFieldprintable)
982             {
983 0         0 $fieldprintable = $record;
984 0         0 $isFieldprintable = $false;
985             }
986             }
987 0         0 $count++;
988             }
989 0 0       0 if ($fieldname)
990             {
991 0         0 $fieldname = $self->xml_unescape($fieldname);
992 0 0       0 if($fieldprintable){
    0          
993 0         0 $record{$fieldname} = $self->xml_unescape($fieldprintable);
994             }elsif($fieldvalue){
995 0         0 $record{$fieldname} = $self->xml_unescape($fieldvalue);
996             }
997             }
998              
999 0         0 return %record;
1000             }
1001              
1002             sub GetRecordAsHTML($$$)
1003             {
1004 0     0 0 0 my($self, $QuickBaseDBid, $rid, $jht) = @_;
1005 0         0 my $content = "$rid";
1006 0 0       0 $content .= "$jht" if $jht ne "";
1007 0         0 $content .= "";
1008 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_GetRecordAsHTML", $content)->content;
1009             }
1010              
1011             sub GetRecordInfo($$)
1012             {
1013 0     0 0 0 my($self, $QuickBaseDBid, $rid) = @_;
1014 0         0 my $content = "$rid";
1015 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_GetRecordInfo", $content)->content;
1016             }
1017              
1018             sub GetRoleInfo($)
1019             {
1020 0     0 1 0 my($self, $QuickBaseDBid) = @_;
1021 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_GetRoleInfo", "")->content;
1022             }
1023              
1024             sub GetSchema
1025             {
1026 0     0 0 0 my($self,$QuickBaseDBid) = @_;
1027 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_GetSchema", "")->content;
1028             }
1029              
1030             sub GetUserInfo($)
1031             {
1032 0     0 1 0 my($self,$email) = @_;
1033            
1034 0         0 my $content = "$email";
1035            
1036 0         0 my $res = $self->PostAPIURL ("main", "API_GetUserInfo", $content)->content;
1037            
1038 0         0 my %userInfo;
1039 0 0       0 if($res =~ /(.*)<\/login>/ ){
1040 0         0 $userInfo{"login"} = $1
1041             }
1042 0 0       0 if($res =~ /(.*)<\/name>/ ){
1043 0         0 $userInfo{"name"} = $1
1044             }
1045 0 0       0 if($res =~ /(.*)<\/firstName>/ ){
1046 0         0 $userInfo{"firstName"} = $1
1047             }
1048 0 0       0 if($res =~ /(.*)<\/lastName>/ ){
1049 0         0 $userInfo{"lastName"} = $1
1050             }
1051 0 0       0 if($res =~ /id=\"(.*)\"/ ){
1052 0         0 $userInfo{"id"} = $1
1053             }
1054 0         0 return %userInfo;
1055             }
1056              
1057             sub GetUserRole($$)
1058             {
1059 0     0 1 0 my($self,$QuickBaseDBid,$userid) = @_;
1060 0         0 my $content = "$userid";
1061 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_GetUserRole", $content)->content;
1062             }
1063              
1064             sub GrantedDBs()
1065             {
1066 0     0 0 0 my($self) = @_;
1067 0         0 $self->PostAPIURL ("main", "API_GrantedDBs", "")->content;
1068             }
1069              
1070             sub ProvisionUser($$$$$)
1071             {
1072 0     0 1 0 my($self, $QuickBaseDBid,$roleid, $email, $fname, $lname) = @_;
1073 0         0 my $content = "";
1074 0         0 $content .= "$roleid";
1075 0         0 $content .= "$email";
1076 0         0 $content .= "$fname";
1077 0         0 $content .= "$lname";
1078 0         0 $content .= "";
1079 0         0 my $res = $self->PostAPIURL ($QuickBaseDBid, "API_ProvisionUser", $content)->content;
1080 0 0       0 if($res =~ /(.*)<\/userid>/ ){
1081 0         0 return $1;
1082             }
1083             else
1084             {
1085 0         0 return "";
1086             }
1087             }
1088              
1089             sub RemoveUserFromRole($$$)
1090             {
1091 0     0 1 0 my($self, $QuickBaseDBid, $userid, $roleid) = @_;
1092 0         0 my $content = "$userid$roleid";
1093 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_RemoveUserFromRole", $content);
1094 0         0 return "";
1095             }
1096              
1097             sub RenameApp($$)
1098             {
1099 0     0 1 0 my($self,$QuickBaseDBid,$newappname) = @_;
1100 0         0 my $content = "" . $self->xml_escape($newappname) . "";
1101 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_RenameApp", $content);
1102 0         0 return "";
1103             }
1104              
1105             sub SendInvitation($$)
1106             {
1107 0     0 1 0 my($self, $QuickBaseDBid, $userid) = @_;
1108 0         0 my $content = "$userid";
1109 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_SendInvitation", $content);
1110 0         0 return "";
1111             }
1112              
1113             sub SetDBvar($$$)
1114             {
1115 0     0 1 0 my($self, $QuickBaseDBid, $varname, $value) = @_;
1116 0         0 my $content = "$varname$value";
1117 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_SetDBvar", $content);
1118 0         0 return "";
1119             }
1120              
1121             sub UserRoles($)
1122             {
1123 0     0 1 0 my($self,$QuickBaseDBid) = @_;
1124 0         0 $self->PostAPIURL ($QuickBaseDBid, "API_UserRoles", "")->content;
1125             }
1126              
1127             sub GetURL($$)
1128             {
1129 0     0 0 0 my($self, $QuickBaseDBid, $action) = @_;
1130 0         0 my $error;
1131              
1132 0 0       0 unless( $action =~ /^act=API_|\&act=API_/i)
1133             {
1134 0         0 $self->{'error'} = "1";
1135 0         0 $self->{'errortext'} = "Error: You're using a QuickBase URL that is not part of the HTTP API. ". $action . "\n"
1136             . "Please use only actions that start with 'API_' i.e. act=API_GetNumRecords.\n"
1137             . "Please refer to the QuickBase HTTP API documentation.";
1138 0         0 return $self->{'errortext'};
1139             }
1140              
1141              
1142 0         0 my $ua = new LWP::UserAgent;
1143 0         0 $ua->agent("QuickBasePerlAPI/2.0");
1144 0 0       0 if ($self->{'proxy'}){
1145 0         0 $ua->proxy(['http','https'], $self->{'proxy'});
1146             }
1147 0         0 my $req = new HTTP::Request;
1148 0         0 $req->method("GET");
1149 0         0 $req->uri($self->URLprefix()."/$QuickBaseDBid?$action");
1150 0 0       0 unless ($self->{'ticket'})
1151             {
1152 0         0 $self->{'ticket'}=$self->getTicket($self->{'username'},$self->{'password'});
1153             }
1154 0         0 $req->header('Cookie' => "TICKET=$self->{'ticket'};");
1155 0         0 $req->header('Accept' => 'text/html');
1156             # send request
1157 0         0 my $res = $ua->request($req);
1158              
1159              
1160             # check the outcome
1161 0 0       0 if ($res->is_error) {
1162 0         0 $self->{'error'} = $res->code;
1163 0         0 $self->{'errortext'} =$res->message;
1164 0         0 return "Error: " . $res->code . " " . $res->message;
1165             }
1166 0         0 return $res->content;
1167             }
1168              
1169             sub GetFile($$$$)
1170             {
1171 0     0 1 0 my($self, $QuickBaseDBid, $filename, $rid, $fid) = @_;
1172 0         0 my $error;
1173 0         0 my $prefix= $self->URLprefix();
1174 0         0 $prefix =~ s/\/db$/\/up/;
1175 0         0 my $ua = new LWP::UserAgent;
1176 0         0 $ua->agent("QuickBasePerlAPI/1.0");
1177 0 0       0 if ($self->{'proxy'}){
1178 0         0 $ua->proxy(['http','https'], $self->{'proxy'});
1179             }
1180 0         0 my $req = new HTTP::Request;
1181 0         0 $req->method("GET");
1182              
1183 0         0 $req->uri($prefix."/$QuickBaseDBid/g/r".$self->encode32($rid)."/e".$self->encode32($fid)."/");
1184              
1185              
1186 0 0       0 unless ($self->{'ticket'})
1187             {
1188 0         0 $self->{'ticket'}=$self->getTicket($self->{'username'},$self->{'password'});
1189             }
1190 0         0 $req->header('Accept' => '*/*');
1191 0         0 $req->header('Cookie' => "TICKET=$self->{'ticket'};");
1192              
1193             # send request
1194 0         0 my $res = $ua->request($req);
1195              
1196             # check the outcome
1197 0 0       0 if ($res->is_error) {
1198 0         0 $self->{'error'} = $res->code;
1199 0         0 $self->{'errortext'} =$res->message;
1200 0         0 return ("Error: " . $res->code . " " . $res->message, $res->headers);
1201             }
1202 0         0 return ($res->content, $res->headers);
1203             }
1204              
1205             sub PostURL($$$$)
1206             {
1207 0     0 0 0 my $self = shift;
1208 0         0 my $QuickBaseDBid = shift;
1209 0         0 my $action = shift;
1210 0         0 my $content = shift;
1211 0   0     0 my $content_type = shift || 'application/x-www-form-urlencoded';
1212              
1213 0         0 my $ua = new LWP::UserAgent;
1214 0 0       0 if ($self->{'proxy'}){
1215 0         0 $ua->proxy(['http','https'], $self->{'proxy'});
1216             }
1217 0         0 $ua->agent("QuickBasePerlAPI/1.0");
1218 0         0 my $req = new HTTP::Request;
1219 0         0 $req->method("POST");
1220 0         0 $req->uri($self->URLprefix."/$QuickBaseDBid?$action");
1221 0 0       0 unless ($self->{'ticket'})
1222             {
1223 0         0 $self->{'ticket'}=$self->getTicket($self->{'username'},$self->{'password'});
1224             }
1225 0         0 $req->header('Cookie' => "TICKET=$self->{'ticket'};");
1226 0         0 $req->content_type($content_type);
1227              
1228             #This is where we post the info for the new record
1229              
1230 0         0 $req->content($content);
1231 0         0 my $res = $ua->request($req);
1232 0 0       0 if($res->is_error()){
1233 0         0 $self->{'error'} = $res->code;
1234 0         0 $self->{'errortext'} =$res->message;
1235 0         0 return $res;
1236             }
1237 0         0 $res->content =~ /(.*?)<\/errcode>.*?(.*?)<\/errtext>/s ;
1238 0         0 $self->{'error'} = $1;
1239 0         0 $self->{'errortext'} = $2;
1240 0 0       0 if ($res->content =~ /(.*?)<\/errdetail>/s)
1241             {
1242 0         0 $self->{'errortext'} = $1;
1243             }
1244 0         0 return $res;
1245             }
1246              
1247             sub PostAPIURL($$$)
1248             {
1249 1     1 0 3 my($self, $QuickBaseDBid, $action, $content) = @_;
1250 1         8 my $ua = new LWP::UserAgent;
1251 1         4737 $ua->agent("QuickBasePerlAPI/2.0");
1252 1 50       85 if ($self->{'proxy'}){
1253 0         0 $ua->proxy(['http','https'], $self->{'proxy'});
1254             }
1255 1         11 my $req = new HTTP::Request;
1256 1         73 $req->method('POST');
1257 1 50       12 if($self->{'realmhost'})
1258             {
1259 0         0 $req->uri($self->URLprefix()."/$QuickBaseDBid?realmhost=$self->{'realmhost'}");
1260             }
1261             else
1262             {
1263 1         6 $req->uri($self->URLprefix()."/$QuickBaseDBid");
1264             }
1265              
1266 1         28545 $req->content_type('text/xml');
1267 1         61 $req->header('QUICKBASE-ACTION' => "$action");
1268              
1269 1 50 33     98 if ($self->{'apptoken'} ne "" && $self->{'credentials'} !~ //)
1270             {
1271 0         0 $self->{'credentials'} .= "".$self->{'apptoken'}."";
1272             }
1273              
1274 1 50 0     8 if($content =~ /^/)
    0          
1275             {
1276 1         9 $content =~s/^/$self->{'credentials'}/;
1277             }
1278             elsif($content eq "" || !defined($content))
1279             {
1280 0         0 $content ="$self->{'credentials'}";
1281             }
1282 1 50       7 if($content =~ /^/)
1283             {
1284 1         4 $content = "" . $content;
1285             }
1286 1         2 my $res;
1287 1 50       5 if ($self->{'ticket'})
1288             {
1289 0         0 $req->header('Cookie' => "TICKET=$self->{'ticket'};");
1290             }
1291            
1292 1         9 $req->content($content);
1293 1         28 $res = $ua->request($req);
1294 1 50       1226087 if($res->is_error()){
1295 0         0 $self->{'error'} = $res->code;
1296 0         0 $self->{'errortext'} =$res->message;
1297 0         0 return $res;
1298             }
1299 1 50 33     23 if (defined ($res->header('Set-Cookie')) && $res->header('Set-Cookie') =~ /TICKET=(.+?);/)
    50          
1300             {
1301 0         0 $self->{'ticket'} = $1;
1302 0         0 $self->{'credentials'} = "$self->{'ticket'}";
1303             }
1304             elsif ($res->content =~ /(.+?)<\/ticket>/)
1305             {
1306 0         0 $self->{'ticket'} = $1;
1307 0         0 $self->{'credentials'} = "$self->{'ticket'}";
1308             }
1309              
1310 1         125 $res->content =~ /(.*?)<\/errcode>.*?(.*?)<\/errtext>/s;
1311 1         57 $self->{'error'} = $1;
1312 1         5 $self->{'errortext'} = $2;
1313 1 50       6 if ($res->content =~ /(.*?)<\/errdetail>/s)
1314             {
1315 1         20 $self->{'errortext'} = $1;
1316             }
1317 1 50       5 if($self->{'error'} eq '11')
1318             {
1319 0         0 $self->{'errortext'} .= "\nXML request:\n" . $content;
1320             }
1321 1         644 return $res;
1322             }
1323              
1324             sub getoneBaseIDbyName($)
1325             {
1326 0     0 0 0 my ($self, $dbName)= @_;
1327 0         0 return $self->getIDbyName($dbName);
1328             }
1329              
1330             sub getIDbyName($)
1331             {
1332 1     1 1 9 my ($self, $dbName)= @_;
1333 1         2 my $content;
1334 1         3 $content = "".$self->xml_escape($dbName)."";
1335 1         5 my $res = $self->PostAPIURL ("main", "API_FindDBByName", $content);
1336              
1337 1 50       8 if($res->content =~ /(.*)<\/dbid>/ ){
1338 0         0 return $1;
1339             }
1340             else
1341             {
1342 1         68 return "";
1343             }
1344             }
1345              
1346             sub FindDBByName($)
1347             {
1348 0     0 0 0 my ($self, $dbName)= @_;
1349 0         0 $self->getIDbyName($dbName);
1350             }
1351              
1352             sub cloneDatabase ($$$)
1353             {
1354 0     0 1 0 my ($self, $QuickBaseID, $Name, $Description)=@_;
1355 0         0 my $content;
1356 0         0 $content = "".$self->xml_escape($Name)."".$self->xml_escape($Description)."";
1357 0         0 my $res = $self->PostAPIURL ($QuickBaseID, "API_CloneDatabase", $content);
1358 0 0       0 if($res->content =~ /(.*)<\/newdbid>/ ){
1359 0         0 return $1;
1360             }
1361             else
1362             {
1363 0         0 return "";
1364             }
1365             }
1366              
1367             sub createDatabase ($$)
1368             {
1369 0     0 1 0 my ($self, $Name, $Description)=@_;
1370 0         0 my $content;
1371 0         0 $content = "".$self->xml_escape($Name)."".$self->xml_escape($Description)."";
1372 0         0 my $res = $self->PostAPIURL ("main", "API_CreateDatabase", $content);
1373 0 0       0 if($res->content =~ /(.*)<\/dbid>/ ){
1374 0         0 my $dbid = $1;
1375 0 0       0 if($res->content =~ /(.*)<\/appdbid>/ ){
1376 0         0 return ($dbid,$1);
1377             }
1378             else
1379             {
1380 0         0 return $1;
1381             }
1382             }
1383             else
1384             {
1385 0         0 return "";
1386             }
1387             }
1388              
1389             sub addField ($$$$)
1390             {
1391 0     0 1 0 my ($self, $QuickBaseID, $label, $type, $mode)=@_;
1392 0         0 my $content;
1393 0         0 $content = "$type";
1394 0 0       0 if ($mode)
1395             {
1396 0         0 $content .= "virtual";
1397             }
1398             else
1399             {
1400 0         0 $content .= "";
1401             }
1402 0         0 my $res = $self->PostAPIURL ($QuickBaseID, "API_AddField", $content);
1403 0 0       0 if($res->content =~ /(.*)<\/fid>/ ){
1404 0         0 return $1;
1405             }
1406             else
1407             {
1408 0         0 return "";
1409             }
1410             }
1411              
1412             sub deleteField ($$)
1413             {
1414 0     0 1 0 my ($self, $QuickBaseID, $fid)=@_;
1415 0         0 my $content;
1416 0         0 $content = "$fid";
1417 0         0 my $res = $self->PostAPIURL ($QuickBaseID, "API_DeleteField", $content);
1418             }
1419              
1420             sub setFieldProperties ($$%)
1421             {
1422 0     0 1 0 my ($self, $QuickBaseID, $fid, %properties)=@_;
1423 0         0 my $content;
1424             my $property;
1425 0         0 my $value;
1426 0         0 $content = "$fid";
1427 0         0 foreach $property (keys %properties)
1428             {
1429 0         0 $content .= "<$property>".$self->xml_escape($properties{$property})."";
1430             }
1431 0         0 $content .= "";
1432 0         0 my $res = $self->PostAPIURL ($QuickBaseID, "API_SetFieldProperties", $content);
1433 0 0       0 if($res->content =~ /(.*)<\/fid>/ ){
1434 0         0 return $1;
1435             }
1436             else
1437             {
1438 0         0 return "";
1439             }
1440             }
1441              
1442              
1443             sub purgeRecords ($$)
1444             {
1445 0     0 0 0 my ($self, $QuickBaseID, $query)=@_;
1446              
1447 0         0 my $content;
1448 0 0       0 if ($query =~ /^\{.*\}$/)
    0          
1449             {
1450 0         0 $content = "$query";
1451             }
1452             elsif ($query =~ /^\d+$/)
1453             {
1454 0         0 $content = "$query";
1455             }
1456             else
1457             {
1458 0         0 $content = "$query";
1459             }
1460 0         0 my $res = $self->PostAPIURL ($QuickBaseID, "API_PurgeRecords", $content);
1461 0 0       0 if($res->content =~ /(.*)<\/num_records_deleted>/ ){
1462 0         0 return $1;
1463             }
1464             else
1465             {
1466 0         0 return "";
1467             }
1468             }
1469              
1470             sub DoQuery ($$$$$)
1471             {
1472 0     0 0 0 my ($self, $QuickBaseID, $query, $clist, $slist, $options)=@_;
1473 0         0 return $self->doQuery ($QuickBaseID, $query, $clist, $slist, $options);
1474             }
1475              
1476             sub doQuery ($$$$$)
1477             {
1478 0     0 1 0 my ($self, $QuickBaseID, $query, $clist, $slist, $options)=@_;
1479              
1480 0         0 my $content;
1481             my $result;
1482 0         0 my @result;
1483 0         0 my $record={};
1484 0         0 my $field;
1485             my @labels;
1486 0         0 my $fieldvalue;
1487 0         0 my $counter = 0;
1488 0         0 my $numfields;
1489             my $i;
1490              
1491 0 0       0 if ($query =~ /^\{.*\}$/)
    0          
1492             {
1493 0         0 $content = "$query";
1494             }
1495             elsif ($query =~ /^\d+$/)
1496             {
1497 0         0 $content = "$query";
1498             }
1499             else
1500             {
1501 0         0 $content = "$query";
1502             }
1503              
1504 0         0 $content .= "structured$clist$slist$options";
1505 0         0 $result = $self->PostAPIURL ($QuickBaseID, "API_DoQuery", $content)->content;
1506 0         0 @labels = $result =~ /
1507 0         0 $numfields = @labels;
1508 0         0 for $i (0 .. $numfields)
1509             {
1510 0         0 $labels[$i] = $self->xml_unescape($labels[$i]);
1511             }
1512 0         0 foreach $fieldvalue ( $result =~ /(.*?)<\/f>|/sg)
1513             {
1514 0 0       0 unless ($counter % $numfields)
1515             {
1516 0 0       0 if ($counter > 0)
1517             {
1518 0         0 push (@result, $record);
1519             }
1520 0         0 $record={};
1521             }
1522 0         0 $record->{$labels[$counter % $numfields]}=$self->xml_unescape($fieldvalue);
1523 0         0 $counter++;
1524             }
1525 0 0       0 if ($counter)
1526             {
1527 0         0 push (@result, $record);
1528             }
1529 0         0 return @result;
1530             }
1531              
1532             sub getCompleteCSV ($)
1533             {
1534 0     0 1 0 my ($self, $QuickBaseID)=@_;
1535 0         0 my $content;
1536 0         0 my $clist="";
1537 0         0 my $fid;
1538             my @ids;
1539 0         0 my $result;
1540 0         0 $result = $self->PostAPIURL ($QuickBaseID, "API_GetSchema", "")->content;
1541 0         0 @ids = $result =~ /]*\sid="(\d+)"/sig;
1542 0         0 foreach $fid (@ids){
1543 0         0 $clist .= "$fid.";
1544             }
1545 0         0 $content .= "{'0'.CT.''}$clistcsv";
1546 0         0 return $self->PostAPIURL ($QuickBaseID, "API_GenResultsTable", $content)->content;
1547             }
1548              
1549             sub GetRIDs ($)
1550             {
1551 0     0 1 0 my ($self, $QuickBaseID) = @_;
1552 0         0 my $content="";
1553 0         0 my $fid;
1554 0         0 $self->PostAPIURL($QuickBaseID,"API_GetSchema",$content)->content =~ /
1555 0         0 $fid = $1;
1556 0         0 $content = "{'0'.CT.''}$fid$fid";
1557 0         0 my @rids = $self->PostAPIURL($QuickBaseID,"API_DoQuery",$content)->content =~ /([0-9]+)<\/record_id_>/sg;
1558 0         0 return @rids;
1559             }
1560              
1561             sub EditRecord ($$%)
1562             {
1563 0     0 1 0 my ($self, $QuickBaseID, $rid, %recorddata) = @_;
1564 0         0 my $name;
1565 0         0 my $content = "$rid";
1566 0         0 my $tag;
1567              
1568 0         0 foreach $name (keys(%recorddata))
1569             {
1570 0         0 $tag=$name;
1571 0         0 $tag =~tr/A-Z/a-z/;
1572 0         0 $tag=~s/[^a-z0-9]/_/g;
1573 0         0 $content .= $self->createFieldXML($tag, $recorddata{$name});
1574             }
1575 0         0 $content .= "";
1576 0         0 my $res = $self->PostAPIURL ($QuickBaseID, "API_EditRecord", $content);
1577 0         0 return $res->content;
1578             }
1579              
1580             sub EditRecordWithUpdateID ($$$%)
1581             {
1582 0     0 0 0 my ($self, $QuickBaseID, $rid, $update_id, %recorddata) = @_;
1583 0         0 my $name;
1584 0         0 my $content = "$rid";
1585 0         0 my ($value, $tag);
1586 0         0 $content .= "$update_id";
1587              
1588              
1589 0         0 foreach $name (keys(%recorddata))
1590             {
1591 0         0 $value = $recorddata{$name};
1592 0         0 $value = $self->xml_escape($value);
1593 0         0 $tag=$name;
1594 0         0 $tag =~tr/A-Z/a-z/;
1595 0         0 $tag=~s/[^a-z0-9]/_/g;
1596              
1597 0         0 $content .= $self->createFieldXML($tag, $recorddata{$name});
1598             }
1599              
1600 0         0 $content .= "";
1601 0         0 my $res = $self->PostAPIURL ($QuickBaseID, "API_EditRecord", $content);
1602 0         0 return $res->content;
1603             }
1604              
1605              
1606             sub ImportFromCSV ($$$$)
1607             {
1608 0     0 0 0 my ($self, $QuickBaseID, $CSVData, $clist, $skipfirst) = @_;
1609 0         0 my $content = "$clist";
1610              
1611 0         0 $content .= "";
1612 0 0       0 if($skipfirst)
1613             {
1614 0         0 $content .= "1";
1615             }
1616 0         0 $content .= "";
1617 0         0 my $res = $self->PostAPIURL ($QuickBaseID, "API_ImportFromCSV", $content);
1618 0         0 return $res->content;
1619             }
1620              
1621              
1622             sub GetNextField ($$$$)
1623             {
1624 0     0 0 0 my ($self, $datapointer, $delim, $offsetpointer, $fieldpointer)=@_;
1625 0         0 my $BEFORE_FIELD=0;
1626 0         0 my $IN_QUOTED_FIELD=1;
1627 0         0 my $IN_UNQUOTED_FIELD=2;
1628 0         0 my $DOUBLE_QUOTE_TEST=3;
1629 0         0 my $c="";
1630 0         0 my $state = $BEFORE_FIELD;
1631 0         0 my $p = $$offsetpointer;
1632 0         0 my $endofdata = length($$datapointer);
1633 0         0 my $false=0;
1634 0         0 my $true=1;
1635              
1636              
1637 0         0 $$fieldpointer = "";
1638              
1639 0         0 while ($true)
1640             {
1641 0 0       0 if ($p >= $endofdata)
1642             {
1643             # File, line and field are done
1644 0         0 $$offsetpointer = $p;
1645 0         0 return $false;
1646             }
1647              
1648 0         0 $c = substr($$datapointer, $p, 1);
1649              
1650 0 0       0 if($state == $DOUBLE_QUOTE_TEST)
    0          
    0          
    0          
1651             {
1652             # These checks are ordered by likelihood */
1653 0 0 0     0 if ($c eq $delim)
    0          
    0          
1654             {
1655             # Field is done; delimiter means more to come
1656 0         0 $$offsetpointer = $p + 1;
1657 0         0 return $true;
1658             }
1659             elsif ($c eq "\n" || $c eq "\r")
1660             {
1661             # Line and field are done
1662 0         0 $$offsetpointer = $p + 1;
1663 0         0 return $false;
1664             }
1665             elsif ($c eq '"')
1666             {
1667             # It is doubled, so append one quote
1668 0         0 $$fieldpointer .= '"';
1669 0         0 $p++;
1670 0         0 $state = $IN_QUOTED_FIELD;
1671             }
1672             else
1673             {
1674             # !!! Shouldn't have anything else after an end quote!
1675             # But do something reasonable to recover: go into unquoted mode
1676 0         0 $$fieldpointer .= $c;
1677 0         0 $p++;
1678 0         0 $state = $IN_UNQUOTED_FIELD;
1679             }
1680             }
1681             elsif($state == $BEFORE_FIELD)
1682             {
1683             # These checks are ordered by likelihood */
1684 0 0 0     0 if ($c eq $delim)
    0          
    0          
    0          
1685             {
1686             # Field is blank; delimiter means more to come
1687 0         0 $$offsetpointer = $p + 1;
1688 0         0 return $true;
1689             }
1690             elsif ($c eq '"')
1691             {
1692             # Found the beginning of a quoted field
1693 0         0 $p++;
1694 0         0 $state = $IN_QUOTED_FIELD;
1695             }
1696             elsif ($c eq "\n" || $c eq "\r")
1697             {
1698             # Field is blank and line is done
1699 0         0 $$offsetpointer = $p + 1;
1700 0         0 return $false;
1701             }
1702             elsif ($c eq ' ')
1703             {
1704             # Ignore leading spaces
1705 0         0 $p++;
1706             }
1707             else
1708             {
1709             # Found some other character, beginning an unquoted field
1710 0         0 $$fieldpointer.=$c;
1711 0         0 $p++;
1712 0         0 $state = $IN_UNQUOTED_FIELD;
1713             }
1714             }
1715             elsif ($state == $IN_UNQUOTED_FIELD)
1716             {
1717             # These checks are ordered by likelihood */
1718 0 0 0     0 if ($c eq $delim)
    0          
1719             {
1720             # Field is done; delimiter means more to come
1721 0         0 $$offsetpointer = $p + 1;
1722 0         0 return $true;
1723             }
1724             elsif ($c eq "\n" || $c eq "\r")
1725             {
1726             # Line and field are done
1727 0         0 $$offsetpointer = $p + 1;
1728 0         0 return $false;
1729             }
1730             else
1731             {
1732             # Found some other character, add it to the field
1733 0         0 $$fieldpointer.=$c;
1734 0         0 $p++;
1735             }
1736             }
1737             elsif($state == $IN_QUOTED_FIELD)
1738             {
1739 0 0       0 if ($c eq '"')
1740             {
1741 0         0 $p++;
1742 0         0 $state = $DOUBLE_QUOTE_TEST;
1743             }
1744             else
1745             {
1746             # Found some other character, add it to the field
1747 0         0 $$fieldpointer.=$c;
1748 0         0 $p++;
1749             }
1750             }
1751             }
1752             }
1753              
1754             sub GetNextLine ($$$$$$)
1755             {
1756 0     0 0 0 my ($self, $data, $delim, $offsetpointer, $fieldpointer, $line, $lineIsEmptyPtr)=@_;
1757 0         0 my $false=0;
1758 0         0 my $true=1;
1759              
1760 0         0 undef(@$line);
1761             # skip any empty lines
1762 0   0     0 while ($$offsetpointer < length($$data) && ((substr($$data, $$offsetpointer, 1) eq "\r") || (substr($$data, $$offsetpointer, 1) eq "\n")))
      0        
1763             {
1764 0         0 $$offsetpointer++;
1765             }
1766              
1767 0 0       0 if ($$offsetpointer >= length($$data))
1768             {
1769 0         0 return $false;
1770             }
1771              
1772 0         0 $$lineIsEmptyPtr = $true;
1773 0         0 my $moreToCome;
1774 0         0 do {
1775 0         0 $moreToCome = $self->GetNextField ($data, $delim, $offsetpointer, $fieldpointer);
1776 0         0 push (@$line, $$fieldpointer);
1777 0 0       0 if ($$fieldpointer)
1778             {
1779 0         0 $$lineIsEmptyPtr = $false;
1780             }
1781             }
1782             while ($moreToCome);
1783              
1784 0         0 return $true;
1785             }
1786              
1787              
1788             sub ParseDelimited ($$)
1789             {
1790 0     0 0 0 my ($self, $data, $delim)=@_;
1791 0         0 my @output;
1792             my @line;
1793 0         0 my $offset =0;
1794              
1795 0         0 my $field="";
1796 0         0 my $lineEmpty=1;
1797 0         0 my $maxsize = 0;
1798 0         0 my $numfields=0;
1799 0         0 my $i;
1800              
1801             # Parse lines until the eof is hit
1802 0         0 while ($self->GetNextLine (\$data, $delim, \$offset, \$field, \@line, \$lineEmpty))
1803             {
1804 0 0       0 unless($lineEmpty)
1805             {
1806 0         0 push (@output, [@line]);
1807 0         0 $numfields=@line;
1808 0 0       0 if ($numfields > $maxsize)
1809             {
1810 0         0 $maxsize = $numfields;
1811             }
1812             }
1813             }
1814              
1815              
1816             # If there are any lines which are shorter than the longest
1817             # lines, fill them out with "" entries here. This simplifies
1818             # checking later.
1819 0         0 foreach $i(@output)
1820             {
1821 0         0 while (@$i < $maxsize)
1822             {
1823 0         0 push (@$i, "");
1824             }
1825             }
1826              
1827 0         0 return @output;
1828              
1829             }
1830             sub xml_escape ($) {
1831 3     3 0 5 my ($self, $rest) = @_;
1832 3 50       7 unless(defined($rest)){return "";}
  0         0  
1833 3         8 $rest =~ s/&/&/g;
1834 3         4 $rest =~ s/
1835 3         6 $rest =~ s/>/>/g;
1836 3         6 $rest =~ s/([^;\/?:@&=+\$,A-Za-z0-9\-_.!~*'()# ])/$XMLescapes{$1}/g;
1837 3         9 return $rest;
1838             }
1839              
1840             sub xml_unescape ($) {
1841 0     0 0   my ($self, $rest) = @_;
1842 0 0         unless(defined($rest)){return "";}
  0            
1843 0           $rest =~ s//\n/ig;
1844 0           $rest =~ s/</
1845 0           $rest =~ s/>/>/g;
1846 0           $rest =~ s/&/&/g;
1847 0           $rest =~ s/'/'/g;
1848 0           $rest =~ s/"/"/g;
1849 0           $rest =~ s/&#([0-9]{2,3});/chr($1)/eg;
  0            
1850 0           return $rest;
1851             }
1852              
1853             sub encode32 ($){
1854 0     0 0   my ($self, $number) = @_;
1855 0           my $result = "";
1856 0           while ($number > 0){
1857 0           my $remainder = $number % 32;
1858 0           $number = ($number - $remainder)/32;
1859 0           $result = $self->hash32($remainder) . $result;
1860             }
1861 0           return $result;
1862             }
1863              
1864             sub hash32 ($){
1865 0     0 0   my ($self, $number) = @_;
1866 0 0         if($number == 0) {return 'a';}
  0            
1867 0 0         if($number == 1) {return 'b';}
  0            
1868 0 0         if($number == 2) {return 'c';}
  0            
1869 0 0         if($number == 3) {return 'd';}
  0            
1870 0 0         if($number == 4) {return 'e';}
  0            
1871 0 0         if($number == 5) {return 'f';}
  0            
1872 0 0         if($number == 6) {return 'g';}
  0            
1873 0 0         if($number == 7) {return 'h';}
  0            
1874 0 0         if($number == 8) {return 'i';}
  0            
1875 0 0         if($number == 9) {return 'j';}
  0            
1876 0 0         if($number == 10) {return 'k';}
  0            
1877 0 0         if($number == 11) {return 'm';}
  0            
1878 0 0         if($number == 12) {return 'n';}
  0            
1879 0 0         if($number == 13) {return 'p';}
  0            
1880 0 0         if($number == 14) {return 'q';}
  0            
1881 0 0         if($number == 15) {return 'r';}
  0            
1882 0 0         if($number == 16) {return 's';}
  0            
1883 0 0         if($number == 17) {return 't';}
  0            
1884 0 0         if($number == 18) {return 'u';}
  0            
1885 0 0         if($number == 19) {return 'v';}
  0            
1886 0 0         if($number == 20) {return 'w';}
  0            
1887 0 0         if($number == 21) {return 'x';}
  0            
1888 0 0         if($number == 22) {return 'y';}
  0            
1889 0 0         if($number == 23) {return 'z';}
  0            
1890 0 0         if($number == 24) {return '2';}
  0            
1891 0 0         if($number == 25) {return '3';}
  0            
1892 0 0         if($number == 26) {return '4';}
  0            
1893 0 0         if($number == 27) {return '5';}
  0            
1894 0 0         if($number == 28) {return '6';}
  0            
1895 0 0         if($number == 29) {return '7';}
  0            
1896 0 0         if($number == 30) {return '8';}
  0            
1897 0 0         if($number == 31) {return '9';}
  0            
1898             }
1899              
1900              
1901              
1902             sub unencode32 ($){
1903 0     0 0   my ($self, $number) = @_;
1904 0           my $result = 0;
1905 0           while ($number ne ""){
1906 0           my $l = length($number);
1907 0           my $firstchar = substr($number, 0, 1);
1908 0           $result = ($result * 32) + $self->unhash32($firstchar);
1909 0           $number = substr($number, 1, $l-1);
1910             }
1911 0           return $result;
1912             }
1913              
1914              
1915              
1916             sub unhash32 ($) {
1917 0     0 0   my ($self, $number) = @_;
1918 0 0         if($number eq 'a') {return 0;}
  0            
1919 0 0         if($number eq 'b') {return 1;}
  0            
1920 0 0         if($number eq 'c') {return 2;}
  0            
1921 0 0         if($number eq 'd') {return 3;}
  0            
1922 0 0         if($number eq 'e') {return 4;}
  0            
1923 0 0         if($number eq 'f') {return 5;}
  0            
1924 0 0         if($number eq 'g') {return 6;}
  0            
1925 0 0         if($number eq 'h') {return 7;}
  0            
1926 0 0         if($number eq 'i') {return 8;}
  0            
1927 0 0         if($number eq 'j') {return 9;}
  0            
1928 0 0         if($number eq 'k') {return 10;}
  0            
1929 0 0         if($number eq 'm') {return 11;}
  0            
1930 0 0         if($number eq 'n') {return 12;}
  0            
1931 0 0         if($number eq 'p') {return 13;}
  0            
1932 0 0         if($number eq 'q') {return 14;}
  0            
1933 0 0         if($number eq 'r') {return 15;}
  0            
1934 0 0         if($number eq 's') {return 16;}
  0            
1935 0 0         if($number eq 't') {return 17;}
  0            
1936 0 0         if($number eq 'u') {return 18;}
  0            
1937 0 0         if($number eq 'v') {return 19;}
  0            
1938 0 0         if($number eq 'w') {return 20;}
  0            
1939 0 0         if($number eq 'x') {return 21;}
  0            
1940 0 0         if($number eq 'y') {return 22;}
  0            
1941 0 0         if($number eq 'z') {return 23;}
  0            
1942 0 0         if($number eq '2') {return 24;}
  0            
1943 0 0         if($number eq '3') {return 25;}
  0            
1944 0 0         if($number eq '4') {return 26;}
  0            
1945 0 0         if($number eq '5') {return 27;}
  0            
1946 0 0         if($number eq '6') {return 28;}
  0            
1947 0 0         if($number eq '7') {return 29;}
  0            
1948 0 0         if($number eq '8') {return 30;}
  0            
1949 0 0         if($number eq '9') {return 31;}
  0            
1950             }
1951              
1952             sub createFieldXML($$)
1953             {
1954 0     0 0   my($self, $tag, $value) = @_;
1955 0           my $nameattribute;
1956 0 0         if($tag =~ /^[1-9]\d*$/)
1957             {
1958 0           $nameattribute = "fid";
1959             }
1960             else
1961             {
1962 0           $nameattribute = "name";
1963             }
1964 0 0         if(ref($value) eq "ARRAY")
1965             {
1966 0 0         if($$value[0] =~ /^file/i)
1967             {
1968             #This is a file attachment!
1969 0           my $filename = "";
1970 0           my $buffer = "";
1971 0           my $filecontents = "";
1972 0 0         if($$value[1] =~ /[\\\/]([^\/\\]+)$/)
1973             {
1974 0           $filename = $1;
1975             }
1976             else
1977             {
1978 0           $filename = $$value[1];
1979             }
1980 0 0         unless(open(FORUPLOADTOQUICKBASE, "<$$value[1]"))
1981             {
1982 0           $filecontents = encode_base64("Sorry QuickBase could not open the file '$$value[1]' for input, for upload to this field in this record.", "");
1983             }
1984 0           binmode FORUPLOADTOQUICKBASE;
1985 0           while (read(FORUPLOADTOQUICKBASE, $buffer, 60*57))
1986             {
1987 0           $filecontents .= encode_base64($buffer, "");
1988             }
1989 0           close FORUPLOADTOQUICKBASE;
1990 0           return "xml_escape($filename)."\">".$filecontents."";
1991             }
1992             }
1993             else
1994             {
1995 0           $value = $self->xml_escape($value);
1996 0           return "$value";
1997             }
1998             }
1999              
2000              
2001             1;