From 6d20e7f5dfef9fa60f62032ca7c3621d5c3335d0 Mon Sep 17 00:00:00 2001 From: Doug Bell Date: Sun, 19 Apr 2009 02:09:34 +0000 Subject: [PATCH] added: ThingyRecord allows you to sell records in a Thing (like a classified ad) --- docs/changelog/7.x.x.txt | 2 + .../root_import_thingyrecord-templates.wgpkg | Bin 0 -> 1331 bytes ...al-items_my-purchases-detail-default.wgpkg | Bin 0 -> 2053 bytes docs/upgrades/upgrade_7.7.3-7.7.4.pl | 38 +- lib/WebGUI/Account/Shop.pm | 12 + lib/WebGUI/Asset/Sku/ThingyRecord.pm | 708 ++++++++++++++++++ .../Sku/ThingyRecord/Record.pm | 44 ++ lib/WebGUI/Form/SelectBox.pm | 29 +- lib/WebGUI/Form/ThingFieldsList.pm | 23 + lib/WebGUI/i18n/English/Asset_ThingyRecord.pm | 81 ++ ...w_Activity_ExpirePurchasedThingyRecords.pm | 53 ++ .../build/thingyRecord/thingyRecord.js | 32 + 12 files changed, 1014 insertions(+), 8 deletions(-) create mode 100644 docs/upgrades/packages-7.7.4/root_import_thingyrecord-templates.wgpkg create mode 100644 docs/upgrades/packages-7.7.4/shopping-cart-collateral-items_my-purchases-detail-default.wgpkg create mode 100644 lib/WebGUI/Asset/Sku/ThingyRecord.pm create mode 100644 lib/WebGUI/AssetCollateral/Sku/ThingyRecord/Record.pm create mode 100644 lib/WebGUI/Form/ThingFieldsList.pm create mode 100644 lib/WebGUI/i18n/English/Asset_ThingyRecord.pm create mode 100644 lib/WebGUI/i18n/English/Workflow_Activity_ExpirePurchasedThingyRecords.pm create mode 100644 www/extras/yui-webgui/build/thingyRecord/thingyRecord.js diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 9819179d2..d6133cdc3 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -31,6 +31,8 @@ - fixed #10213: RssFeed aspect now checks canView and gives HTTP Basic Auth box to login - added Survey Number type. Text number entry that uses slider restrictions as constraints (server and client side). You can also use the arrow keys to increment or decrement the number enter. If slider constraints are blank, no rules applied. + - added: ThingyRecord allows you to sell records in a Thingy (like a classified ad) + 7.7.3 - fixed #10094: double explanation in thread help - rfe #9612: Carousel Wobject (was Widget Wobject) (SDH Consulting Group) diff --git a/docs/upgrades/packages-7.7.4/root_import_thingyrecord-templates.wgpkg b/docs/upgrades/packages-7.7.4/root_import_thingyrecord-templates.wgpkg new file mode 100644 index 0000000000000000000000000000000000000000..42073ef2df9effc59bdb5c50603e55429b8c6e13 GIT binary patch literal 1331 zcmV-31*Fc$&ni zZJ^9*{`(%=2{ureGTTa{Rr639`}myW@7%s@=idJ*l}fJT?!5S<+59; zxlW~0&E?_6DdqBT$ZC5(+K0=*qb{d@S8MvEc9hp;^|vDbmP^}T zIgQ`cM^Ia@)lA&sUFAU<{)$^(jDOXw)pL13^ZlRqivRZeyZ#my{`mw^C~!d;8sb8} zmH+GS-_%mVXo8s_n9EfqCMA8slMqe2M1)GbP%bEWx#uyA1oro)@@dL@6zq4L*%&!pX8aI*Bpsam-HrMMvKI%*RZfY~SpgEy7QT@8fv!l7x2{6;wUcZbnq2>@XrwqCDt?eWfopipF9xAFPWj`N)7s5)u}s1NX!zutD^a*ie^58$%@C4MHC>~GKa%8%3b5p%42FV*P=w==sh}SjlI1Z{*;+sel;zki50$ew_Tyem*%EJ~X}K6Q{9t z^~LMe!lAi9!bmKnshbx{^92ZjClzF~{{HFv&(4D7GSe$>$pS#*y7^yqU3Y>1)oK;z zWB&haOgA~V>HPmc&G_zl6pR}K+NpLAD$kRfNpJ6|WR+t!;{zIDHQbkGd(p=aXcUUA zxzRtCBVjgn=0>~KA=ixdHA6e=tE$KC$fkNv9`YP>*{Zk4h;1W3BJp*++h_e)3&3jL zqp@H#U86>g$K9PGEUIHG6iJH~4Oli4fy2=2wG!owJTkYs zM)d<_(R~0k4zpB)Y?^rwhhc(zA6VG${X(f>_HH^WSCTh>ELle|(3>>%_t zOntE~Sr$xpsCHfnDJ0y-C=c>-!tIv&u; zX?z4FpsBOfd1Lr;kX=D@W;>$A`G~%Xl|D6|_wUZipQHN4<0oL2>hZ5{0l%UvOEA7001Xfm{tG) literal 0 HcmV?d00001 diff --git a/docs/upgrades/packages-7.7.4/shopping-cart-collateral-items_my-purchases-detail-default.wgpkg b/docs/upgrades/packages-7.7.4/shopping-cart-collateral-items_my-purchases-detail-default.wgpkg new file mode 100644 index 0000000000000000000000000000000000000000..63ae743e7447d195806d428e109649770c0235e8 GIT binary patch literal 2053 zcmV+g2>SOQiwFP!000001MOICZxcBX?q`0*D(Iv`B26|)TUc_H3WaNREkv|XB*GzQ z*U9?udiQK^(}4KzH-6c*lWb@}RJso&QeHgcnP*-+Gwa=*yMJ}N-GjZoogMXk&~@KI z{p#+7VR+CR9PAAa!kqyA0Dk~m?*3H@i8w%;{tL^tM{oBvj}=EWJrZy9DY-)kMyC3~vVdqZ%I8{{PBLiArZ4IM3_H4=>3;8q3nT=Vc7B*Wy4vfH zl7p~3dHT4xLGRM5>Bl3-TXL%B43XwV0WG>6BMt{QrxeB-j%XX&cqTg8qFvZgLbMYs z5uxxkMJ1I^77-^TNifp_Lw|k5bD682yE+G{IifgCo+m`Ejys?iT9KWthEOGg7?MfA zpyS4xdej+8VE#y^0m~(cu^oWMa%o=%-2#R|*kstR5wG}%9Z+C*4Leqt`LB!9Fzwgs z^^^oc{yt^kB8F6vLiBfia06Cm6U-l8fIA;N37j5HeW{6u!%o_-5E|(fdLmxqxa1rH zVHwn;gfilAh=P>kDVXns#)!q3)?40idbp0uFa^949C#(q;v9AmfV#y4C9OSBjcB#| z!O@f)4%K9`>K5R|wHnRf@GIJ@X*Ht)1Q1e9nHvS73`S46Nu;4}DT=T_<$VCNKO|sK z3Y=_`OPC7jGtGF6lTx>)E!9YLgT*Gnnz*GPkrI7KgAB_w--xwhJLcpX^D{KT^m`S2 z+sT!J5m#qzyRa2#kzpopfo{2e069E{l>(&eBfiPm>S3)E=Z47#KJ1imcJyI1pG$?q zPMUtYtHZKV04fVDca6cFD^riW>SMPYnMoD^IbQ$w4@0T~6?_M#YR`6{EHsHG@Z?w( zXs^QP>22H!piWUcEkOzE+7+k=G1NM)+r%nXbBhslWi)MiCli z1GDO8bzp8|E>wDN&HZnQ7_^4P)*C8EuLb8eT&B~QT;5t6#3cR%zFzK7(CeJX;7{di zweF%Ygt0VP)bUZRmgZ15^=wc(d^J*um*{E?d{d}U!D>gk@Tw^J|KNW^qq`<}t}@AX zIM+eqoK4BBgtC08?k+$!s`0?21j6lHV4fw!8NfaqPI#w*OPl2Lxl9_%_f4>1cv215 z!J)R+qQeh?zZ*=hoVKiZdDKwDoosNAQ=n6_$%Bri%EJb(`hbcIQECtPk^w5B7q-i| z0Xi_q-O0Xx~zuP<$J+r zDPprtmx||kLZ$?+3)O{V*c*WQ{y}fwY1D(Jrk*c?YDyBpu^zvJ-;d4w{lIOo)AU<7 zEqdF5BN3ryY*nR=I!6(oNbwS;<|&EwD68q-RV8%oQXj7GL1K!N-hm3ZalQ6$MfLN^ z1>7rY`d~2do)rqj;qp@xHn=#7PA<>S)*VjZaL1o2XD{8+5@CI%<$AU$nAiqM)52b_ z_o&-@+}m>w;`o{fnDLK+*D~2yc}zr%puSewconfig->addToHash('assets','WebGUI::Asset::Sku::ThingyRecord', { + category => "shop", + }); + + # Install ThingyRecord + $session->db->write( <<'ENDSQL' ); + CREATE TABLE IF NOT EXISTS ThingyRecord ( + assetId CHAR(22) BINARY NOT NULL, + revisionDate BIGINT NOT NULL, + templateIdView CHAR(22) BINARY, + thingId CHAR(22) BINARY, + thingFields LONGTEXT, + thankYouText LONGTEXT, + price FLOAT, + duration BIGINT, + PRIMARY KEY (assetId, revisionDate) + ); +ENDSQL + # Install collateral + use WebGUI::AssetCollateral::Sku::ThingyRecord::Record; + WebGUI::AssetCollateral::Sku::ThingyRecord::Record->crud_createTable($session); + # Update workflow + my $activityClass = 'WebGUI::Workflow::Activity::ExpirePurchasedThingyRecords'; + $session->config->addToArray( 'workflow/None', $activityClass ); + my $workflow = WebGUI::Workflow->new( $session, 'pbworkflow000000000004' ); + my $activity = $workflow->addActivity( $activityClass ); + $activity->set('title', "Expire Purchased Thingy Records"); + $activity->set('description', "Expire any expired thingy records. Send notifications of imminent expiration."); + print "DONE!\n" unless $quiet; +} # -------------- DO NOT EDIT BELOW THIS LINE -------------------------------- diff --git a/lib/WebGUI/Account/Shop.pm b/lib/WebGUI/Account/Shop.pm index fdd545565..d14b8b214 100644 --- a/lib/WebGUI/Account/Shop.pm +++ b/lib/WebGUI/Account/Shop.pm @@ -338,12 +338,24 @@ sub www_viewTransaction { phoneNumber => $item->get('shippingPhoneNumber'), }); } + + # Post purchase actions + my $actionsLoop = []; + my $actions = $item->getSku->getPostPurchaseActions( $item ); + for my $label ( keys %{$actions} ) { + push @{$actionsLoop}, { + label => $label, + url => $actions->{$label}, + } + } + push @items, { %{$item->get}, viewItemUrl => $url->page('shop=transaction;method=viewItem;transactionId='.$transaction->getId.';itemId='.$item->getId), price => sprintf("%.2f", $item->get('price')), itemShippingAddress => $address, orderStatus => $i18n->get($item->get('orderStatus')), + actionsLoop => $actionsLoop, }; } $var{items} = \@items; diff --git a/lib/WebGUI/Asset/Sku/ThingyRecord.pm b/lib/WebGUI/Asset/Sku/ThingyRecord.pm new file mode 100644 index 000000000..a83d42f2f --- /dev/null +++ b/lib/WebGUI/Asset/Sku/ThingyRecord.pm @@ -0,0 +1,708 @@ +package WebGUI::Asset::Sku::ThingyRecord; + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2009 Plain Black Corporation. + ------------------------------------------------------------------- + Please read the legal notices (docs/legal.txt) and the license + (docs/license.txt) that came with this distribution before using + this software. + ------------------------------------------------------------------- + http://www.plainblack.com info@plainblack.com + ------------------------------------------------------------------- + +=cut + +use strict; +use Tie::IxHash; +use base 'WebGUI::Asset::Sku'; +use WebGUI::Utility; + +# Collateral data class... very long name. Zoffix eat your heart out. +my $RECORD_CLASS = 'WebGUI::AssetCollateral::Sku::ThingyRecord::Record'; + +=head1 NAME + +Package WebGUI::Asset::Sku::ThingyRecord + +=head1 DESCRIPTION + +Purchase a record in a thingy. + +=head1 SYNOPSIS + +use WebGUI::Asset::ThingyRecord; + + +=head1 METHODS + +These methods are available from this class: + +=cut + +#------------------------------------------------------------------- + +=head2 definition ( session, definition ) + +=head3 session + +=head3 definition + +A hash reference passed in from a subclass definition. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" ); + tie my %properties, 'Tie::IxHash', ( + templateIdView => { + tab => "display", + fieldType => "template", + namespace => "ThingyRecord/View", + label => $i18n->get('templateIdView label'), + hoverHelp => $i18n->get('templateIdView description'), + }, + thingId => { + tab => "properties", + fieldType => "selectBox", + options => $class->getThingOptions($session), + label => $i18n->get('thingId label'), + hoverHelp => $i18n->get('thingId description'), + extras => q{onchange="WebGUI.ThingyRecord.getThingFields(this.options[this.selectedIndex].value,'thingFields_formId')"}, + }, + thingFields => { + tab => "properties", + fieldType => "selectList", + options => {}, # populated by ajax call + label => $i18n->get('thingFields label'), + hoverHelp => $i18n->get('thingFields description'), + }, + thankYouText => { + tab => "properties", + fieldType => "HTMLArea", + defaultValue=> $i18n->get('default thank you message','Asset_Product') . " ^ViewCart;", + label => $i18n->get("thank you message",'Asset_Product'), + hoverHelp => $i18n->get("thank you message help",'Asset_Product'), + }, + price => { + tab => "properties", + fieldType => "float", + label => $i18n->get('10',"Asset_Product"), #Price + hoverHelp => $i18n->get('price','Asset_Product'), + }, + duration => { + tab => "properties", + fieldType => "interval", + defaultValue=> 60*60*24*7, # One week + label => $i18n->get('duration label'), + hoverHelp => $i18n->get('duration description'), + }, + ); + push @{$definition}, { + assetName => $i18n->get('assetName'), + icon => 'ThingyRecord.gif', + autoGenerateForms => 1, + tableName => 'ThingyRecord', + className => __PACKAGE__, + properties => \%properties, + }; + return $class->SUPER::definition( $session, $definition ); +} ## end sub definition + +#---------------------------------------------------------------------------- + +=head2 appendVarsEditRecord ( var, recordId ) + +Get the template variables for the form to edit the record. Does not include +the header or footer! + +=cut + +sub appendVarsEditRecord { + my ( $self, $var, $recordId ) = @_; + my $session = $self->session; + my $thingy = $self->getThingy; + my $record = {}; + if ( $recordId ) { + # Get an existing record + $record = $self->getThingRecord( $self->get('thingId'), $recordId ); + if ( !%$record ) { # Record is hidden + $record = JSON->new->decode( + $RECORD_CLASS->new( $session, $recordId )->get('fields') + ); + } + } + + my $fields = $self->getThingFields( $self->get('thingId') ); + my @allowed = split "\n", $self->get('thingFields'); + for my $field ( @{$fields} ) { + next unless grep { $_ eq $field->{fieldId} } @allowed; + $field->{value} = $record->{'field_'.$field->{fieldId}} || $field->{defaultValue}; + my %fieldProperties = ( + "input" => $thingy->getFormElement($field), + "value" => $thingy->getFieldValue($field->{value}, $field), + "label" => $field->{label}, + "isHidden" => ($field->{status} eq 'hidden'), + "isVisible" => ($field->{status} eq "visible"), + "isRequired" => ($field->{status} eq "required"), + "pretext" => $field->{pretext}, + "subtext" => $field->{subtext}, + ); + push @{$var->{form_fields}}, { + map { "field_" . $_ => $fieldProperties{$_} } keys %fieldProperties + }; + # Add a way to get the field outside of the loop + # TODO + } + + return $var; +} + +#------------------------------------------------------------------- + +=head2 deleteThingRecord ( thingId, recordId ) + +Delete a record from a thing + +=cut + +sub deleteThingRecord { + my ( $self, $thingId, $recordId ) = @_; + my $db = $self->session->db; + my $dbh = $self->session->db->dbh; + my $tableName = $dbh->quote_identifier( 'Thingy_' . $thingId ); + $db->write( + "DELETE FROM $tableName WHERE thingDataId=?", + [$recordId] + ); +} + +#------------------------------------------------------------------- + +=head2 getEditForm ( ) + +Add the javascript needed for the edit form + +=cut + +sub getEditForm { + my ( $self ) = @_; + $self->session->style->setScript( + $self->session->url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), + { type => "text/javascript" }, + ); + $self->session->style->setScript( + $self->session->url->extras('yui/build/connection/connection-min.js'), + { type => "text/javascript" }, + ); + $self->session->style->setScript( + $self->session->url->extras('yui/build/json/json-min.js'), + { type => "text/javascript" }, + ); + $self->session->style->setScript( + $self->session->url->extras('yui-webgui/build/thingyRecord/thingyRecord.js'), + { type => "text/javascript" }, + ); + return $self->SUPER::getEditForm; +} + +#---------------------------------------------------------------------------- + +=head2 getMaxAllowedInCart ( ) + +One only! + +=cut + +sub getMaxAllowedInCart { + my ( $self ) = @_; + return 1; +} + +#---------------------------------------------------------------------------- + +=head2 getPostPurchaseActions ( item ) + +Return a hash reference of "label" => "url" to do things with this item after +it is purchased. C is the WebGUI::Shop::TransactionItem for this item + +=cut + +sub getPostPurchaseActions { + my ( $self, $item ) = @_; + my $session = $self->session; + my $opts = $self->SUPER::getPostPurchaseActions(); + my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" ); + my $recordId = $item->get('options')->{recordId}; + + $opts->{ $i18n->get('renew') } + = $self->getUrl('func=renew;recordId='.$recordId); + $opts->{ $i18n->get('575', 'WebGUI') } # edit + = $self->getUrl('func=editRecord;recordid='.$recordId); + + return $opts; +} + +#---------------------------------------------------------------------------- + +=head2 getPrice ( ) + +Get the price + +=cut + +sub getPrice { + my ( $self ) = @_; + return $self->get('price'); +} + +#---------------------------------------------------------------------------- + +=head2 getTemplateVars ( ) + +Get common template vars for this asset. + +=cut + +sub getTemplateVars { + my $self = shift; + my $var = $self->get; + $var->{ url } = $self->getUrl; + return $var; +} + +#---------------------------------------------------------------------------- + +=head2 getThingFields ( thingId ) + +Get the fields for a thing. + +=cut + +sub getThingFields { + my ( $self, $thingId ) = @_; + + my $fields = $self->session->db->buildArrayRefOfHashRefs( + 'SELECT * FROM Thingy_fields WHERE thingId = ? ORDER BY sequenceNumber', + [$thingId] + ); + + return $fields; +} + +#---------------------------------------------------------------------------- + +=head2 getThingOptions ( session ) + +Get all the thingys and all the things in them. + +=cut + +sub getThingOptions { + my ( $class, $session ) = @_; + tie my %options, 'Tie::IxHash', ( "" => "" ); + my $thingyIter = WebGUI::Asset->getRoot( $session ) + ->getLineageIterator( ['descendants'], { + includeOnlyClasses => ['WebGUI::Asset::Wobject::Thingy'], + } ); + while ( my $thingy = $thingyIter->() ) { + tie my %things, 'Tie::IxHash', ( + $session->db->buildHash( + "SELECT thingId, label FROM Thingy_things WHERE assetId=?", + [$thingy->getId] + ) + ); + $options{$thingy->get('title')} = \%things; + } + + return \%options; +} + +#---------------------------------------------------------------------------- + +=head2 getThingRecord ( thingId, recordId ) + +Get a row of data from a thing. Returns a hashref + +=cut + +sub getThingRecord { + my ( $self, $thingId, $recordId ) = @_; + my $table = $self->session->db->dbh->quote_identifier( "Thingy_" . $thingId ); + return $self->session->db->quickHashRef( + "SELECT * FROM " . $table . " WHERE thingDataId=?", + [$recordId] + ); +} + +#---------------------------------------------------------------------------- + +=head2 getThingy ( ) + +Get the thingy associated with this ThingyRecord + +=cut + +sub getThingy { + my ( $self ) = @_; + my $thingyId = $self->session->db->quickScalar( + "SELECT assetId FROM Thingy_things WHERE thingId=?", + [$self->get('thingId')], + ); + return WebGUI::Asset->newByDynamicClass( $self->session, $thingyId ); +} + +#------------------------------------------------------------------- + +=head2 onCompletePurchase ( ) + +Purchase completed, add the record. + +=cut + +sub onCompletePurchase { + my ( $self, $item ) = @_; + + my $option = $self->getOptions; + my $record = $RECORD_CLASS->new( $self->session, $option->{recordId} ); + my $now = time; + + if ( $option->{action} eq "buy" ) { + # Update record + $record->update({ + expires => $now + $self->get('duration'), + transactionId => $item->transaction->getId, + isHidden => 0, + }); + + # Add to thingy data + my $data = JSON->new->decode( $record->get('fields') ); + $self->updateThingRecord( $self->get('thingId'), $record->getId, $data ); + } + elsif ( $option->{action} eq "renew" ) { + # Renew a currently active record + if ( $record->get('expires') > $now ) { + $record->update({ + expires => $record->get('expires') + $self->get('duration'), + }); + } + # Renew an expired but not deleted record + else { + $record->update({ + expires => $now + $self->get('duration'), + isHidden => 0, + }); + + # Add to thingy data + my $data = JSON->new->decode( $record->get('fields') ); + $self->updateThingRecord( $self->get('thingId'), $record->getId, $data ); + } + } +} + +#------------------------------------------------------------------- + +=head2 onRemoveFromCart ( ) + +Removed from cart, remove all knowledge + +=cut + +sub onRemoveFromCart { + my ( $self, $item ) = @_; + + # Remove from cart + my $option = $self->getOptions; + if ( $option->{action} eq "buy" ) { + my $record = $RECORD_CLASS->new($self->session,$option->{recordId}); + if ( $record ) { + $record->delete; + } + } +} + +#------------------------------------------------------------------- + +=head2 prepareView ( ) + +See WebGUI::Asset::prepareView() for details. + +=cut + +sub prepareView { + my $self = shift; + $self->SUPER::prepareView(); + my $template = WebGUI::Asset::Template->new( $self->session, $self->get("templateIdView") ); + $template->prepare($self->getMetaDataAsTemplateVariables); + $self->{_viewTemplate} = $template; +} + +#------------------------------------------------------------------- + +=head2 processEditRecordForm ( ) + +Process the edit record form and return the record + +=cut + +sub processEditRecordForm { + my ( $self ) = @_; + my $var = {}; + + my $fields = $self->getThingFields( $self->get('thingId') ); + for my $field ( @{$fields} ) { + my $fieldName = 'field_'.$field->{fieldId}; + my $fieldType = $field->{fieldType}; + $fieldType = "" if ($fieldType =~ m/^otherThing/x); + $var->{ $fieldName } + = $self->session->form->get($fieldName,$fieldType,$field->{defaultValue},$field); + } + + return $var; +} + +#------------------------------------------------------------------- + +=head2 purge ( ) + +Remove all collateral associated with the ThingyRecord sku + +=cut + +sub purge { + my $self = shift; + + my $options = { + constraints => { + 'assetId = ?' => $self->getId, + }, + }; + + my $iter = $RECORD_CLASS->getAllIterator($self->session,$options); + while ( my $item = $iter->() ) { + $item->delete; + } + + # Should we also remove the records from the Thingy? + + return $self->SUPER::purge; +} + +#------------------------------------------------------------------- + +=head2 updateThingRecord ( thingId, data ) + +Update data in a thing + +=cut + +sub updateThingRecord { + my ( $self, $thingId, $recordId, $data ) = @_; + my $db = $self->session->db; + my $dbh = $self->session->db->dbh; + my $tableName = $dbh->quote_identifier('Thingy_'.$thingId); + $data->{ thingDataId } = $recordId; + my $columns = join ",", map { $dbh->quote_identifier( $_ ) } keys %{$data}; + my $values = [ values %{$data} ]; + my $places = join ",", ('?') x @{$values}; + $self->session->db->write( + "REPLACE INTO $tableName ($columns) VALUES ($places)", + $values, + ); +} + +#------------------------------------------------------------------- + +=head2 view ( options ) + +method called by the container www_view method. + +=cut + +sub view { + my ( $self, $options ) = @_; + my $session = $self->session; + my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" ); + my $var = $self->getTemplateVars; + $self->appendVarsEditRecord( $var ); + $var->{ isNew } = 1; + $var->{ message } = $options->{addedToCart} + ? $self->get('thankYouText') + : $options->{message} + ; + + # Add form header, footer, and submit button + $var->{ form_header } + = WebGUI::Form::formHeader( $session, { + action => $self->getUrl('func=buy'), + } ); + + $var->{ form_footer } + = WebGUI::Form::formFooter( $session ); + + $var->{ form_submit } + = WebGUI::Form::submit( $session, { + value => $i18n->get('add to cart','Shop'), + } ); + + return $self->processTemplate( $var, undef, $self->{_viewTemplate} ); +} + +#---------------------------------------------------------------------------- + +=head2 www_buy ( ) + +Create a new record and add it to the cart + +=cut + +sub www_buy { + my ( $self ) = @_; + my $session = $self->session; + + # Get data for row + my $recordFields = $self->processEditRecordForm; + my $recordData = { + userId => $session->user->userId, + assetId => $self->getId, + fields => JSON->new->encode( $recordFields ), + }; + + # Add row to cart collateral + my $record = $RECORD_CLASS->create( $session, $recordData ); + + # Add item to cart with appropriate action and recordId + $self->addToCart({ + action => "buy", + recordId => $record->getId, + }); + + # Return thank you screen + $self->prepareView; + return $self->processStyle( + $self->view({ addedToCart => 1 }) + ); +} + +#---------------------------------------------------------------------------- + +=head2 www_editRecord ( options ) + +Edit the record after is has been purchased. Allow the user to show/hide the +record while it is still active. + +=cut + +sub www_editRecord { + my ( $self, $options ) = @_; + my $session = $self->session; + my $recordId = $session->form->get( 'recordId' ); + my $record = $RECORD_CLASS->new( $session, $recordId ); + return $self->session->privilege->insufficient + unless $self->session->user->userId eq $record->get('userId'); + my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" ); + my $var = $self->getTemplateVars; + $self->appendVarsEditRecord( $var, $recordId ); + $var->{ message } = $options->{message}; + + # Add form header, footer, and submit button + $var->{ form_header } + = WebGUI::Form::formHeader( $session, { + action => $self->getUrl('func=editRecordSave;recordId=' . $recordId), + } ); + + $var->{ form_footer } + = WebGUI::Form::formFooter( $session ); + + $var->{ form_submit } + = WebGUI::Form::submit( $session, { + value => $i18n->get('save','WebGUI'), + } ); + + # Add record information + my $recordData = $record->get; + for my $key ( keys %{$recordData} ) { + $var->{ "record_" . $key } = $recordData->{ $key }; + } + + # Add field to hide/show + # Don't allow user to show expired record + if ( time < $record->get('expires') ) { + $var->{ form_hide } + = WebGUI::Form::yesNo( $session, { + name => "hide", + value => $record->get('isHidden'), + } ); + } + + return $self->processStyle( + $self->processTemplate( $var, $self->get('templateIdView') ) + ); +} + +#---------------------------------------------------------------------------- + +=head2 www_editRecordSave ( ) + +Save the record + +=cut + +sub www_editRecordSave { + my ( $self ) = @_; + my $session = $self->session; + my $form = $self->session->form; + my $recordId = $form->get('recordId'); + my $record = $RECORD_CLASS->new( $session, $recordId ); + return $self->session->privilege->insufficient + unless $self->session->user->userId eq $record->get('userId'); + my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" ); + my $hide = $form->get('hide'); + my $recordData = $self->processEditRecordForm; + $record->update({ + fields => JSON->new->encode( $recordData ), + isHidden => $hide, + }); + + if ( $hide ) { + $self->deleteThingRecord( $self->get('thingId'), $recordId ); + } + else { + $self->updateThingRecord( $self->get('thingId'), $recordId, $recordData ); + } + + return $self->www_editRecord({ message => $i18n->get('saved') }); +} + +#---------------------------------------------------------------------------- + +=head2 www_renew ( ) + +Add more time to an existing record. + +=cut + +sub www_renew { + my ( $self ) = @_; + my $session = $self->session; + my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" ); + my $recordId = $self->session->form->get('recordId'); + my $record = $RECORD_CLASS->new( $session, $recordId ); + return $session->privilege->insufficient + unless $session->user->userId eq $record->get('userId'); + + $self->addToCart({ + action => "renew", + recordId => $recordId, + }); + + return $self->www_editRecord({ message => $i18n->get('renewal added to cart') . ' ^ViewCart;' }); +} + +1; + +#vim:ft=perl diff --git a/lib/WebGUI/AssetCollateral/Sku/ThingyRecord/Record.pm b/lib/WebGUI/AssetCollateral/Sku/ThingyRecord/Record.pm new file mode 100644 index 000000000..791fe3be0 --- /dev/null +++ b/lib/WebGUI/AssetCollateral/Sku/ThingyRecord/Record.pm @@ -0,0 +1,44 @@ +package WebGUI::AssetCollateral::Sku::ThingyRecord::Record; + +use strict; + +use base 'WebGUI::Crud'; + +sub crud_definition { + my ($class, $session) = @_; + my $definition = $class->SUPER::crud_definition($session); + $definition->{tableName} = 'ThingyRecord_record'; + $definition->{tableKey} = 'recordId'; + my $properties = $definition->{properties}; + $properties->{transactionId} = { + fieldType => "hidden", + defaultValue => undef, + }; + $properties->{assetId} = { + fieldType => "hidden", + defaultValue => undef, + }; + $properties->{expires} = { + fieldType => "DateTime", + defaultValue => 0, + }; + $properties->{userId} = { + fieldType => "hidden", + defaultValue => undef, + }; + $properties->{fields} = { + fieldType => 'textarea', + defaultValue => '', + }; + $properties->{isHidden} = { + fieldType => 'yesNo', + defaultValue => 0, + }; + $properties->{sentExpiresNotice} = { + fieldType => 'yesNo', + defaultValue => 0, + }; + return $definition; +} + +1; diff --git a/lib/WebGUI/Form/SelectBox.pm b/lib/WebGUI/Form/SelectBox.pm index f101ff57b..bc3f04d07 100644 --- a/lib/WebGUI/Form/SelectBox.pm +++ b/lib/WebGUI/Form/SelectBox.pm @@ -165,13 +165,28 @@ sub toHtml { my $output = ''."\n"; return $output; } diff --git a/lib/WebGUI/Form/ThingFieldsList.pm b/lib/WebGUI/Form/ThingFieldsList.pm new file mode 100644 index 000000000..ed1cdd7f5 --- /dev/null +++ b/lib/WebGUI/Form/ThingFieldsList.pm @@ -0,0 +1,23 @@ +package WebGUI::Form::ThingFieldsList; + +use strict; +use base 'WebGUI::Form::SelectList'; + + +#---------------------------------------------------------------------------- + +sub www_getThingFields { + my ( $session ) = @_; + + my $thingId = $session->form->get('thingId'); + my %fields + = $session->db->buildHash( + "SELECT fieldId, label FROM Thingy_fields WHERE thingId=?", + [$thingId] + ); + + $session->http->setMimeType( 'application/json' ); + return JSON->new->encode( \%fields ); +} + +1; diff --git a/lib/WebGUI/i18n/English/Asset_ThingyRecord.pm b/lib/WebGUI/i18n/English/Asset_ThingyRecord.pm new file mode 100644 index 000000000..e5e7fc96a --- /dev/null +++ b/lib/WebGUI/i18n/English/Asset_ThingyRecord.pm @@ -0,0 +1,81 @@ +package WebGUI::i18n::English::Asset_ThingyRecord; + +use strict; + +our $I18N = { + assetName => { + message => "Thingy Record", + lastUpdated => 0, + context => "The name of the asset", + }, + + renew => { + message => "Renew", + lastUpdated => 0, + context => "Label for button to renew a subscription", + }, + + saved => { + message => "Saved!", + lastUpdated => 0, + context => "Message to show after ThingyRecord is succesfully updated", + }, + + 'renewal added to cart' => { + message => "Your renewal has been added to your cart.", + lastUpdated => 0, + context => "Message after adding a renewal to the cart.", + }, + + 'templateIdView label' => { + message => "View Template", + lastUpdated => 0, + context => "Label for asset property", + }, + + 'templateIdView description' => { + message => "The template to buy a new ThingyRecord", + lastUpdated => 0, + context => "Description of asset property", + }, + + 'thingId label' => { + message => "Add to Thing", + lastUpdated => 0, + context => "Label for asset property", + }, + + 'thingId description' => { + message => "The thing to purchase a record in", + lastUpdated => 0, + context => "Description of asset property", + }, + + 'thingFields label' => { + message => "Fields to Add", + lastUpdated => 0, + context => "Label for asset property", + }, + + 'thingFields description' => { + message => "The fields to allow the user to add data to", + lastUpdated => 0, + context => "Description of asset property", + }, + + + 'duration label' => { + message => "Duration", + lastUpdated => 0, + context => "Label for asset property", + }, + + 'duration description' => { + message => "Length of a time a ThingyRecord should last", + lastUpdated => 0, + context => "Description of asset property", + }, +}; + +1; +#vim:ft=perl diff --git a/lib/WebGUI/i18n/English/Workflow_Activity_ExpirePurchasedThingyRecords.pm b/lib/WebGUI/i18n/English/Workflow_Activity_ExpirePurchasedThingyRecords.pm new file mode 100644 index 000000000..a23604525 --- /dev/null +++ b/lib/WebGUI/i18n/English/Workflow_Activity_ExpirePurchasedThingyRecords.pm @@ -0,0 +1,53 @@ +package WebGUI::i18n::English::Workflow_Activity_ExpirePurchasedThingyRecords; + +use strict; + +our $I18N = { + 'topicName' => { + message => "Expire Purchased Thingy Records", + lastUpdated => 0, + }, + 'default notification' => { + message => q{Your subscription is about to expire!}, + lastUpdated => 0, + context => "The default notification message when a ThingyRecord is about to expire.", + }, + 'default notification subject' => { + message => q{Important notice about your subscription}, + lastUpdated => 0, + context => "The default notification message subject", + }, + 'notificationOffset label' => { + message => q{Notification Offset}, + lastUpdated => 0, + context => "Label for workflow activity property", + }, + 'notificationOffset description' => { + message => q{The amount of time before the ThingyRecord expires when the notification is sent.}, + lastUpdated => 0, + context => "Description of workflow activity property", + }, + 'notificationMessage label' => { + message => q{Notification Message}, + lastUpdated => 0, + context => "Label for workflow activity property", + }, + 'notificationMessage description' => { + message => q{The message to send for the notification}, + lastUpdated => 0, + context => "Description of workflow activity property", + }, + 'notificationSubject label' => { + message => q{Notification Message Subject}, + lastUpdated => 0, + context => "Label for workflow activity property", + }, + 'notificationSubject description' => { + message => q{The subject of the message to send}, + lastUpdated => 0, + context => "Description of workflow activity property", + }, +}; + +1; +#vim:ft=perl diff --git a/www/extras/yui-webgui/build/thingyRecord/thingyRecord.js b/www/extras/yui-webgui/build/thingyRecord/thingyRecord.js new file mode 100644 index 000000000..43f55a22b --- /dev/null +++ b/www/extras/yui-webgui/build/thingyRecord/thingyRecord.js @@ -0,0 +1,32 @@ + +// Requires YUI Connection and JSON + +if ( typeof WebGUI == "undefined" ) { + WebGUI = {}; +} +if ( typeof WebGUI.ThingyRecord == "undefined" ) { + WebGUI.ThingyRecord = {}; +} + +WebGUI.ThingyRecord.getThingFields += function ( thingId, elementId ) { + + // Callback to populate the form element + var callback = { + success : function (o) { + var el = document.getElementById(elementId); + while ( el.options.length >= 1 ) + el.remove(0); + var fields = YAHOO.lang.JSON.parse( o.responseText ); + for ( var key in fields ) { + el.options[el.options.length] + = new Option( fields[key], key, 1, 1 ); + } + } + }; + + var url = '?op=formHelper;class=ThingFieldsList;sub=getThingFields;thingId=' + + thingId + ; + YAHOO.util.Connect.asyncRequest( 'GET', url, callback ); +};