diff --git a/docs/upgrades/packages-7.5.11/root_import_subscription-default.wgpkg b/docs/upgrades/packages-7.5.11/root_import_subscription-default.wgpkg new file mode 100644 index 000000000..529070909 Binary files /dev/null and b/docs/upgrades/packages-7.5.11/root_import_subscription-default.wgpkg differ diff --git a/docs/upgrades/upgrade_7.5.10-7.5.11.pl b/docs/upgrades/upgrade_7.5.10-7.5.11.pl index e483eeefe..40d67a05f 100644 --- a/docs/upgrades/upgrade_7.5.10-7.5.11.pl +++ b/docs/upgrades/upgrade_7.5.10-7.5.11.pl @@ -18,6 +18,7 @@ use WebGUI::DateTime; use WebGUI::Asset::Sku::Product; use WebGUI::Workflow; use WebGUI::User; +use WebGUI::Utility; use File::Find; use File::Spec; use File::Path; @@ -58,6 +59,7 @@ addCoupon( $session ); addVendors($session); modifyThingyPossibleValues( $session ); removeLegacyTable($session); +migrateSubscriptions( $session ); finish($session); # this line required @@ -1107,6 +1109,223 @@ sub removeLegacyTable { print "Done.\n" unless $quiet; } + + +#------------------------------------------------- +sub migrateSubscriptions { + my $session = shift; + print "\tMigrating subscriptions to the new commerce system..." unless ($quiet); + + # Check if codes are tied to multiple subscriptions. + my ($hasDoubles) = $session->db->buildArray( + 'select count(*) as cnt from subscriptionCodeSubscriptions group by code order by cnt desc' + ); + print "\n\t\t!!WARNING: There are subscription codes that link to multiple subscriptions!!" + ." Please refer to gotcha.txt!\n" if $hasDoubles > 1; + + # Rename old subscription table so we can reuse it for the Sku + $session->db->write('alter table subscription rename to Subscription_OLD'); + + # Create the new subscription table + $session->db->write(<db->write(<db->write(<getImportNode( $session )->addChild({ + className => 'WebGUI::Asset::Wobject::Folder', + menuTitle => 'Migrated subscriptions', + title => 'Migrated subscriptions', + ownerUserId => 3, + }); + + # Migrate all subscriptions + print "\t\tConverting subscriptions to assets:\n"; + my $subscriptions = $session->db->read( 'select * from Subscription_OLD' ); + while (my $subscription = $subscriptions->hashRef) { + # Don't migrate deleted subscriptions + next if $subscription->{ deleted }; + + # Add a new subscription sku + my $sku = $subscriptionsFolder->addChild( + { + className => 'WebGUI::Asset::Sku::Subscription', + ownerUserId => 3, + url => 'subscriptions/'.$subscription->{ description }, + menuTitle => $subscription->{ description }, + title => $subscription->{ description }, + price => $subscription->{ price }, + description => $subscription->{ description }, + subscriptionGroup => $subscription->{ subscriptionGroup }, + duration => $subscription->{ duration }, + executeOnSubscription => $subscription->{ executeOnSubscription }, + karma => $subscription->{ karma }, + templateId => 'eqb9sWjFEVq0yHunGV8IGw', + overrideTaxRate => $subscription->{ useSalesTax } ? 0 : 1, + taxRateOverride => 0, + }, + $subscription->{ subscriptionId }, + ); + + # Log and print migration data + my $message = "Migrated subscription '$subscription->{ description }' ($subscription->{ subscriptionId }) " + . " to '" . $sku->getUrl . "' (" . $sku->getId . ")"; + $session->errorHandler->warn( $message ); + print "\t\t--> $message\n"; + } + $subscriptions->finish; + + # Subscriptions are migrated, now migrate the subscription codes + # First find batches with multiple subscriptions per code + my @multiBatches = $session->db->buildArray( + 'select distinct batchId from subscriptionCode where code in ' + .' (select code from subscriptionCodeSubscriptions group by code having count(subscriptionId) > 1)' + ); + + # Migrate subscription codes batch by batch + print "\t\tMigrating subscription codes.\n"; + my @batches = $session->db->buildArray('select distinct batchId from subscriptionCodeBatch'); + foreach my $batchId ( @batches ) { + my $subscriptionId; + + # Fetch batch properties and the number of code. Discard used or expired codes. + my ($numberOfCodes, $codeLength, $expirationDate, $dateCreated, $name, $description) = + $session->db->quickArray( + 'select count(*), length(t1.code), (t1.dateCreated + t1.expires), ' + .' t1.dateCreated, t2.name, t2.description ' + .' from subscriptionCode as t1, subscriptionCodeBatch as t2 ' + .' where t1.batchId=t2.batchId and t1.batchId=? ' + .' and t1.status=\'Unused\' ' + .' and from_unixtime(t1.dateCreated + t1.expires) > now() ' + .' group by t1.batchId', + [ + $batchId, + ] + ); + + # Skip expired or fully used batches; + next unless $numberOfCodes; + + # Check if the codes in this batch link to multiple subscriptions + if ( isIn( $batchId, @multiBatches ) ) { + my $message = "\t\tBatch $batchId has codes linking to multiple subscriptions:\n"; + + # Find the subscriptions the code in this batch are attached to + my @subscriptions = $session->db->buildArray( + 'select distinct subscriptionId from subscriptionCodeSubscriptions where code in ' + .' (select distinct code from subscriptionCode where batchId=?)', + [ + $batchId, + ] + ); + + # Migrate the codes for the first subscription in the list (this is done below) + $subscriptionId = shift @subscriptions; + + my $subscription = WebGUI::Asset::Sku::Subscription->new($session, $subscriptionId); + $message .= "\t\t--> Keeping codes for subscription " + . "'" . $subscription->get('title') . "' (" . $subscription->getUrl . ") \n"; + + # And generate new codes for the remaining subscriptions + foreach my $assetId ( @subscriptions ) { + my $subscription = WebGUI::Asset::Sku::Subscription->new($session, $assetId); + + $message .= "\t\t--> Generating new codes for subscription " + . "'" . $subscription->get('title') . "' (" . $subscription->getUrl . "): \n"; + + my $batchId = $subscription->generateSubscriptionCodeBatch( + $numberOfCodes, + $codeLength, + $expirationDate, + $name, + $description + ); + + $message .= "\t\t\t" . join( "\n\t\t\t", keys %{ $subscription->getCodesInBatch( $batchId ) } ). "\n"; + } + + # Log and print migration info + $session->errorHandler->warn( $message ); + print $message; + } + else { + $subscriptionId = $session->db->quickScalar( + 'select distinct subscriptionId from subscriptionCodeSubscriptions ' + .' where code in (select code from subscriptionCode where batchId=?)', + [ + $batchId, + ] + ); + } + + # Migrate the batch itself + $session->db->write( + 'insert into Subscription_codeBatch ' + . ' (batchId, name, description, subscriptionId, expirationDate, dateCreated) ' + . ' values (? , ? , ? , ? , ? , ? ) ', + [ + $batchId, + $name, + $description, + $subscriptionId, + $expirationDate, + $dateCreated, + ] + ); + + # Migrate the codes + $session->db->write( + 'insert into Subscription_code (batchId, code, status, dateUsed, usedBy) ' + .' select batchId, code, status, dateUsed, usedBy from subscriptionCode where batchId=?', + [ + $batchId, + ] + ); + } + + print "\tDone.\n"; +} + + # -------------- DO NOT EDIT BELOW THIS LINE -------------------------------- #---------------------------------------------------------------------------- @@ -1115,6 +1334,7 @@ sub addPackage { my $session = shift; my $file = shift; +print "Importing package: $file..."; # Make a storage location for the package my $storage = WebGUI::Storage->createTemp( $session ); $storage->addFileFromFilesystem( $file ); @@ -1124,6 +1344,8 @@ sub addPackage { # Make the package not a package anymore $package->update({ isPackage => 0 }); + +print "Done\n"; } #------------------------------------------------- diff --git a/lib/WebGUI/Asset/Sku/Subscription.pm b/lib/WebGUI/Asset/Sku/Subscription.pm new file mode 100644 index 000000000..30fbfa9be --- /dev/null +++ b/lib/WebGUI/Asset/Sku/Subscription.pm @@ -0,0 +1,898 @@ +package WebGUI::Asset::Sku::Subscription; + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2008 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::Asset::Template; +use WebGUI::Form; +use WebGUI::Shop::Pay; + +=head1 NAME + +Package WebGUI::Asset::Sku::Subscription + +=head1 DESCRIPTION + +This asset makes subscriptionss possible. + +=head1 SYNOPSIS + +use WebGUI::Asset::Sku::Subscription; + +=head1 METHODS + +These methods are available from this class: + +=cut + + +#------------------------------------------------------------------- + +=head2 apply ( [ $userId ] ) + +Method for subscribing a user. Adds user to the proper group and sets the expiration date, +adds karma to the user for purchasing a subscription, and then runs any external commands +as specified by the executeOnSubscription property. Macros in executeOnSubscription are +expanded before the command is executed. + +=head3 userId + +ID of the user purchasing the subscription. If omitted, uses the current user as +specified by the session variable. + +=cut + +sub apply { + my $self = shift; + my $userId = shift || $self->session->user->userId; + my $groupId = $self->get('subscriptionGroup'); + + # Make user part of the right group and adjust the expiration date + my $group = WebGUI::Group->new($self->session,$groupId); + $group->addUsers( [$userId], $self->getExpirationOffset ); + + # Add karma to the user's account + WebGUI::User->new($self->session,$userId)->karma($self->get('karma'), 'Subscription', 'Added for purchasing subscription '.$self->get('title')); + + # Process the executeOnPurchase field + my $command = $self->get('executeOnSubscription'); + WebGUI::Macro::process($self->session,\$command); + system($command) if ($self->get('executeOnSubscription') ne ""); +} + + +#------------------------------------------------------------------- + +=head2 definition + +Adds templateId, thankYouMessage, and defaultPrice fields. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my %properties; + tie %properties, 'Tie::IxHash'; + my $i18n = WebGUI::International->new($session, "Asset_Subscription"); + %properties = ( + templateId => { + tab => "display", + fieldType => "template", + namespace => "Subscription", + defaultValue => 'eqb9sWjFEVq0yHunGV8IGw', + label => $i18n->get("template"), + hoverHelp => $i18n->get("template help"), + }, + thankYouMessage => { + tab => "properties", + defaultValue => $i18n->get("default thank you message"), + fieldType => "HTMLArea", + label => $i18n->get("thank you message"), + hoverHelp => $i18n->get("thank you message help"), + }, + + price => { + fieldType => 'float', + label => $i18n->get('subscription price'), + hoverHelp => $i18n->get('subscription price description'), + defaultValue => '0.00', + }, + subscriptionGroup => { + fieldType => 'group', + label => $i18n->get('subscription group'), + hoverHelp => $i18n->get('subscription group description'), + defaultvalue => [ 2 ] + }, + duration => { + fieldType => 'selectBox', + label => $i18n->get('subscription duration'), + hoverHelp => $i18n->get('subscription duration description'), + defaultValue => 'Monthly', + options => WebGUI::Shop::Pay->new( $session )->getRecurringPeriodValues, + }, + executeOnSubscription => { + fieldType => 'text', + label => $i18n->get('execute on subscription'), + hoverHelp => $i18n->get('execute on subscription description'), + defaultValue => '', + }, + ); + + # Show the karma field only if karma is enabled + if ($session->setting->get("useKarma")) { + $properties{ karma } = { + type => 'integer', + label => $i18n->get('subscription karma'), + hoverHelp => $i18n->get('subscription karma description'), + defaultvalue => 0, + }; + } + + push(@{$definition}, { + assetName => $i18n->get('assetName'), + icon => 'subscription.gif', + autoGenerateForms => 1, + tableName => 'Subscription', + className => 'WebGUI::Asset::Sku::Subscription', + properties => \%properties, + }); + return $class->SUPER::definition($session, $definition); +} + +#------------------------------------------------------------------- + +=head2 generateSubscriptionCode ( length ) + +Generates a subscription code with the given length. Does not save to the db. + +=head3 length + +The length of the code. + +=cut + +sub generateSubscriptionCode { + my $self = shift; + my $codeLength = shift || 64; + my @codeElements = ('A'..'Z', 'a'..'z', 0..9, '-'); + my $code; + + for (1 .. $codeLength) { + $code .= $codeElements[rand(63)]; + } + + return $code; +} + +#------------------------------------------------------------------- + +=head2 generateSubscriptionCodeBatch ( numberOfCodes, length, expirationDate, name, description ) + +Creates a batch of subscription codes. + +=head3 numberOfCodes + +The number of codes in this batch. + +=head3 length + +The length of each code. + +=head3 expirationDate + +The epoch for the date when the codes expire. + +=head3 name + +The name for this batch. + +=head3 description + +The batch description. + +=cut + +sub generateSubscriptionCodeBatch { + my $self = shift; + my $numberOfCodes = shift; + my $codeLength = shift; + my $expirationDate = shift; + my $name = shift || 'Untitled'; + my $description = shift; + my $session = $self->session; + my $now = time; + + # Create a new batch and write its properties to the db + my $batchId = $session->db->setRow( 'Subscription_codeBatch', 'batchId', { + batchId => 'new', + name => $name, + description => $description, + subscriptionId => $self->getId, + expirationDate => $expirationDate, + dateCreated => $now + }); + + # Generate the codes for this batch + for ( 1 .. $numberOfCodes ) { + # Generate a code + my $code = $self->generateSubscriptionCode( $codeLength ); + + # Make sure the code is unique + while ( $session->db->quickScalar("select code from Subscription_code where code=?", [ $code ] ) ) { + $code = $self->generateSubscriptionCode( $codeLength ); + } + + # Code is unique so store it + $session->db->setRow( 'Subscription_code', 'code', + { + batchId => $batchId, + status => 'Unused', + dateUsed => 0, + usedBy => 0, + }, + $code + ); + } + + return $batchId; +} + +#------------------------------------------------------------------- + +=head2 getAdminConsoleWithSubmenu ( ) + +Returns an admin console with management links added to the submenu. + +=cut + +sub getAdminConsoleWithSubmenu { + my $self = shift; + my $session = $self->session; + my $ac = $self->getAdminConsole; + my $i18n = WebGUI::International->new( $session, 'Asset_Subscription' ); + + $ac->addSubmenuItem( $self->getUrl('func=createSubscriptionCodeBatch'), $i18n->get('generate batch')); + $ac->addSubmenuItem( $self->getUrl('func=listSubscriptionCodes'), $i18n->get('manage codes') ); + $ac->addSubmenuItem( $self->getUrl('func=listSubscriptionCodeBatches'), $i18n->get('manage batches')); + + return $ac; +} + +#------------------------------------------------------------------- + +=head2 getCode ( code ) + +Returns a hashref with the properties of the passed code. + +=head3 code + +The code for which the properties hould be returned. + +=cut + +sub getCode { + my $self = shift; + my $code = shift; + my $session = $self->session; + + my $codeProperties = $session->db->quickHashRef( + " select * from Subscription_code as t1, Subscription_codeBatch as t2 " + ." where t1.batchId = t2.batchId and t1.code=? and t2.subscriptionId=?", + [ + $code, + $self->getId, + ] + ); + + return $codeProperties || {}; +} + +#------------------------------------------------------------------- + +=head2 getCodesInBatch ( batchId ) + +Returns a hashref of hashrefs containing the properties of all the codes in a batch. +The format is as follows: + + $codes->{ CODE }->{ PROPERTY } + +=head3 batchId + +The id of the batch. + +=cut + +sub getCodesInBatch { + my $self = shift; + my $batchId = shift; + my $session = $self->session; + my $codes = {}; + + my $sth = $session->db->read('select * from Subscription_code where batchId=?', [ + $batchId + ]); + + while (my $row = $sth->hashRef) { + $codes->{ $row->{code} } = $row; + } + + $sth->finish; + + return $codes; +} + +#------------------------------------------------------------------- + +=head2 getConfiguredTitle + +Returns title + price + +=cut + +sub getConfiguredTitle { + my $self = shift; + return $self->getTitle." (".$self->getOptions->{price}.")"; +} + +#------------------------------------------------------------------- + +sub getExpirationOffset { + my $self = shift; + my $duration = shift || $self->get('duration'); + + # y, m, d + return $self->session->datetime->addToDate( 1, 0, 0, 7 ) - 1 if $duration eq 'Weekly'; + return $self->session->datetime->addToDate( 1, 0, 0, 14 ) - 1 if $duration eq 'BiWeekly'; + return $self->session->datetime->addToDate( 1, 0, 0, 28 ) - 1 if $duration eq 'FourWeekly'; + return $self->session->datetime->addToDate( 1, 0, 1, 0 ) - 1 if $duration eq 'Monthly'; + return $self->session->datetime->addToDate( 1, 0, 3, 0 ) - 1 if $duration eq 'Quarterly'; + return $self->session->datetime->addToDate( 1, 0, 6, 0 ) - 1 if $duration eq 'HalfYearly'; + return $self->session->datetime->addToDate( 1, 1, 0, 0 ) - 1 if $duration eq 'Yearly'; + + # TODO: Throw exception +} + +#------------------------------------------------------------------- + +=head2 getPrice + +Returns configured price, 0.00 if neither of those are available. + +=cut + +sub getPrice { + my $self = shift; + return $self->get('price') || 0.00; +} + +#------------------------------------------------------------------- + +=head2 onCompletePurchase + +Applies the first term of the subscription. This method is called when the payment is successful. + +=cut + +sub onCompletePurchase { + my $self = shift; + + $self->apply; +} + +#------------------------------------------------------------------- + +=head2 prepareView + +Prepares the template. + +=cut + +sub prepareView { + my $self = shift; + $self->SUPER::prepareView(); + my $templateId = $self->get("templateId"); + my $template = WebGUI::Asset::Template->new($self->session, $templateId); + $template->prepare; + $self->{_viewTemplate} = $template; +} + +#------------------------------------------------------------------- + +=head2 redeemCode ( code ) + +Redeems a subscription code. Returns undef if redemption is successful, otherwise an error message is returned. + +=head3 code + +The code that should be redeemed. + +=cut + +sub redeemCode { + my $self = shift; + my $code = shift; + my $session = $self->session; + my $i18n = my $i18n = WebGUI::International->new($session, "Asset_Subscription"); + + my $properties = $self->getCode( $code ); + + if ($properties->{ status } eq 'Unused' && $properties->{ expirationDate } >= time) { + # Code is ok so apply the subscription + $self->apply; + + # Set code to Used + $session->db->write("update Subscription_code set status='Used', dateUsed=? where code =?", [ + time, + $code, + ]); + } else { + return $i18n->get('redeem code failure'); + } + + return undef; +} + +#------------------------------------------------------------------- + +=head2 view + +Displays the donation form. + +=cut + +sub view { + my ($self) = @_; + my $session = $self->session; + + my $i18n = WebGUI::International->new($session, "Asset_Subscription"); + my %var = ( + formHeader => WebGUI::Form::formHeader($session, { action=>$self->getUrl }) + . WebGUI::Form::hidden( $session, { name=>"func", value=>"purchaseSubscription" }), + formFooter => WebGUI::Form::formFooter($session), + purchaseButton => WebGUI::Form::submit( $session, { value => $i18n->get("purchase button") }), + hasAddedToCart => $self->{_hasAddedToCart}, + codeControls => join (' · ', ( + ''.$i18n->get('generate batch').'', + ''.$i18n->get('manage codes').'', + ''.$i18n->get('manage batches').'', + )), + redeemCodeLabel => $i18n->get('redeem code'), + redeemCodeUrl => $self->getUrl('func=redeemSubscriptionCode'), + ); + return $self->processTemplate(\%var,undef,$self->{_viewTemplate}); +} + +#---------------------------------------------------------------------------- + +=head2 www_createSubscriptionCodeBatch ( $error ) + +Form to accept parameters to create a batch of subscription codes. + +=head3 error + +An HTML array ref of an error message to be returned to the user. + +=cut + +sub www_createSubscriptionCodeBatch { + my $self = shift; + my $error = shift; + my $session = $self->session; + my $i18n = WebGUI::International->new($session, "Asset_Subscription"); + + # Check privs + return $session->privilege->adminOnly() unless $self->canEdit; + + # Generate error message if errors occurred + my $errorMessage = $i18n->get('create batch error').'
  • '.join('
  • ', @{$error}).'
' if ($error); + + # Generate the properties form for this subscription code batch + my $f = WebGUI::HTMLForm->new( $session ); + $f->submit; + $f->hidden( + -name => 'func', + -value => 'createSubscriptionCodeBatchSave' + ); + $f->integer( + -name => 'noc', + -label => $i18n->get('noc'), + -hoverHelp => $i18n->get('noc description'), + -value => $session->form->process("noc") || 1 + ); + $f->integer( + -name => 'codeLength', + -label => $i18n->get('code length'), + -hoverHelp => $i18n->get('code length description'), + -value => $session->form->process("codeLength") || 64 + ); + $f->interval( + -name => 'expires', + -label => $i18n->get('codes expire'), + -hoverHelp => $i18n->get('codes expire description'), + -value => $session->form->process("expires") || $session->datetime->intervalToSeconds(1, 'months') + ); + $f->text( + -name => 'name', + -label => $i18n->get('batch name'), + -hoverHelp => $i18n->get('batch name description'), + -value => $session->form->process('name'), + ); + $f->textarea( + -name => 'description', + -label => $i18n->get('batch description'), + -hoverHelp => $i18n->get('batch description description'), + -value => $session->form->process("description"), + ); + $f->submit; + + return $self->getAdminConsoleWithSubmenu->render( $errorMessage.$f->print, $i18n->get('create batch menu') ); +} + +=head2 www_createSubscriptionCodeBatchSave ( ) + +Method that accepts the form parameters to create a batch of subscription codes. + +=cut + +#------------------------------------------------------------------- +sub www_createSubscriptionCodeBatchSave { + my $self = shift; + my $session = $self->session; + my $i18n = WebGUI::International->new($session, "Asset_Subscription"); + + # Check privs + return $session->privilege->adminOnly() unless $self->canEdit; + + my $numberOfCodes = $session->form->process("noc"); + my $description = $session->form->process("description"); + my $name = $session->form->process("name"); + my $expires = $session->form->interval('expires'); + my $batchId = $session->id->generate; + my $codeLength = $session->form->process("codeLength"); + + # Sanity check input + my @error; + push(@error, $i18n->get('no description error')) unless ($description); + push(@error, $i18n->get('code length error')) + unless ( + $codeLength >= 10 + && $codeLength <= 64 + && $codeLength =~ m/^\d\d$/ + ); + + # Return an error message if an error occurred + return $self->www_createSubscriptionCodeBatch( \@error ) if @error; + + # Create the code batch + $self->generateSubscriptionCodeBatch( + $numberOfCodes, + $codeLength, + time() + $expires, + $name, + $description, + ); + + return $self->www_listSubscriptionCodeBatches; +} + +#------------------------------------------------------------------- + +=head2 www_deleteSubscriptionCodeBatch ( ) + +Deletes a batch of subscription codes. + +=cut + +sub www_deleteSubscriptionCodeBatch { + my $self = shift; + my $session = $self->session; + my $batchId = $session->form->process('bid'); + + return $session->privilege->insufficient unless $self->canEdit; + + # Remove code batch properties and codes in batch + $session->db->write( 'delete from Subscription_codeBatch where batchId=?', [ $batchId ] ); + $session->db->write( 'delete from Subscription_code where batchId=?', [ $batchId ] ); + + return $self->www_listSubscriptionCodeBatches; +} + +#------------------------------------------------------------------- + +=head2 www_deleteSubscriptionCodes ( ) + +Deletes subscription codes based on either their creation date or their usage date. + +=cut + +sub www_deleteSubscriptionCodes { + my $self = shift; + my $session = $self->session; + + return $session->privilege->insufficient unless $self->canEdit; + + my $selectBy = $session->form->process('selection'); + + if ($selectBy eq 'dc') { + # Delete codes by creation date + my $from = $session->form->date( 'dcStart' ); + my $to = $session->form->date( 'dcStop' ); + + $session->db->write( + 'delete from Subscription_code where batchId in ' + .' ( select batchId from Subscription_codeBatch ' + .' where dateCreated >= ? and dateCreated <= ? and subscriptionId=? ' + .' )', + [ + $from, + $to, + $self->getId, + ] + ); + } + elsif ($selectBy eq 'du') { + # Delete codes by usage date + my $from = $session->form->date( 'duStart' ); + my $to = $session->form->date( 'duStop' ); + + $session->db->write( + 'delete from Subscription_code where dateUsed >= ? and dateUsed <= ? and batchId in ' + .'( select batchId from Subscription_codeBatch where subscriptionId=? )', + [ + $from, + $to, + ] + ); + } + + return $self->www_listSubscriptionCodes; +} + +#------------------------------------------------------------------- + +=head2 www_listSubscriptionCodeBatches + +Display a list of code batches for this subscription. + +=cut + +sub www_listSubscriptionCodeBatches { + my $self = shift; + my $session = $self->session; + my $i18n = WebGUI::International->new( $session, "Asset_Subscription" ); + + # Check privs + return $session->privilege->insufficient unless $self->canEdit; + + # Set up a paginator to paginate the list of batches + my $p = WebGUI::Paginator->new( $session, $self->getUrl('func=listSubscriptionCodeBatches') ); + $p->setDataByQuery( 'select * from Subscription_codeBatch where subscriptionId=?', undef, 1, [ + $self->getId, + ]); + + # Fetch the list of batches at the current paginition index + my $batches = $p->getPageData; + + my $output = $p->getBarTraditional($session->form->process("pn")); + $output .= ''; + foreach my $batch ( @{$batches} ) { + $output .= ''; + $output .= ''; + $output .= ''; + } + $output .= '
'; + $output .= $session->icon->delete( + 'func=deleteSubscriptionCodeBatch;bid='.$batch->{batchId}, + $self->getUrl, + $i18n->get('delete batch confirm')); + $output .= '' . $batch->{ description } . '' + . '' + . $i18n->get('list codes in batch').'
'; + $output .= $p->getBarTraditional($session->form->process("pn")); + + $output = $i18n->get('no subscription code batches') unless ( @{$batches} ); + + return $self->getAdminConsoleWithSubmenu->render( $output, $i18n->get('manage batches') ); +} + +#------------------------------------------------------------------- + +=head2 www_listSubscriptionCodes ( ) + +Displays a list of subscription codes for this subscription. + +=cut + +sub www_listSubscriptionCodes { + my $self = shift; + my $session = $self->session; + my $i18n = WebGUI::International->new($session, "Asset_Subscription"); + + my ($p, $codes, $output, $where, $ops, $delete); + return $session->privilege->insufficient unless $self->canEdit; + + my $dcStart = $session->form->date('dcStart'); + my $dcStop = $session->datetime->addToTime($session->form->date('dcStop'), 23, 59); + my $duStart = $session->form->date('duStart'); + my $duStop = $session->datetime->addToTime($session->form->date('duStop'), 23, 59); + my $batchId = $session->form->process('bid'); + my $selection = $session->form->process('selection'); + my $batches = + $session->db->buildHashRef('select batchId, description from Subscription_codeBatch where subscriptionId=?', + [ + $self->getId, + ]); + + + # Build a subscription code selection form + my $f = WebGUI::HTMLForm->new( $session ); + $f->hidden( + name => 'func', + value => 'listSubscriptionCodes', + ); + #--- Selection by date created + $f->readOnly( + label => + WebGUI::Form::radio( $session, { name => 'selection', value => 'du', checked => ($selection eq 'du') } ) + . $i18n->get('selection used'), + value => + WebGUI::Form::date( $session, { name => 'duStart', value=> $duStart } ) + . ' ' . $i18n->get( 'and' ) . ' ' + . WebGUI::Form::date( $session, { name => 'duStop', value=> $duStop } ), + ); + #--- Selection by date used + $f->readOnly( + label => + WebGUI::Form::radio( $session, { name => 'selection', value => 'dc', checked => ($selection eq 'dc') } ) + . $i18n->get('selection created'), + value => + WebGUI::Form::date( $session, { name => 'dcStart', value=> $dcStart } ) + . ' ' . $i18n->get( 'and' ) . ' ' + . WebGUI::Form::date( $session, { name => 'dcStop', value=> $dcStop } ), + ); + #--- Selection by batch + $f->readOnly( + label => + WebGUI::Form::radio( $session, { name => 'selection', value => 'b', checked => ($selection eq 'b') } ) + . $i18n->get('selection batch id'), + value => + WebGUI::Form::selectBox( $session, { name => 'bid', value => $batchId, options => $batches } ), + ); + #--- Submit button + $f->submit( + value => $i18n->get('select'), + ); + + if ($session->form->process("selection") eq 'du') { + $where = " and dateUsed >= ".$session->db->quote($duStart)." and dateUsed <= ".$session->db->quote($duStop); + $ops = ';duStart='.$session->form->process('duStart').';duStop='.$session->form->process('duStop').';selection=du'; + $delete = ''.$i18n->get('delete codes').''; + } elsif ($session->form->process("selection") eq 'dc') { + $where = " and dateCreated >= ".$session->db->quote($dcStart)." and dateCreated <= ".$session->db->quote($dcStop); + $ops = ';dcStart='.$session->form->process('dcStart').';dcStop='.$session->form->process('dcStop').';selection=dc'; + $delete = ''.$i18n->get('delete codes').''; + } elsif ($session->form->process("selection") eq 'b') { + $where = " and t1.batchId=".$session->db->quote($session->form->process("bid")); + $ops = ';bid='.$session->form->process("bid").';selection=b'; + $delete = ''.$i18n->get('delete codes').''; + } else { + return $self->getAdminConsoleWithSubmenu->render( $output, $i18n->get('listSubscriptionCodes title') ); + } + + $p = WebGUI::Paginator->new( $session, $self->getUrl('func=listSubscriptionCodes'.$ops) ); + $p->setDataByQuery( + "select t1.*, t2.* from Subscription_code as t1, Subscription_codeBatch as t2 ". + " where t1.batchId=t2.batchId and subscriptionId=?".$where, + undef, undef, + [ + $self->getId, + ] + ); + + $codes = $p->getPageData; + + $output = $i18n->get('selection message'); + $output .= $f->print; + $output .= '
'.$delete.'
' if ($delete); + $output .= $p->getBarTraditional($session->form->process("pn")); + $output .= '
'; + $output .= ''; + $output .= ''; + $output .= ''; $output .= ''; + foreach (@{$codes}) { + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + } + $output .= '
'.$i18n->get('batch id').''.$i18n->get('code').''.$i18n->get('creation date'). + ''.$i18n->get('dateUsed').''.$i18n->get('status').'
'.$_->{batchId}.''.$_->{code}.''.$session->datetime->epochToHuman($_->{dateCreated}).''; + $output .= $session->datetime->epochToHuman($_->{dateUsed}) if ($_->{dateUsed}); + $output .= ''.$_->{status}.'
'; + $output .= $p->getBarTraditional($session->form->process("pn")); + + return $self->getAdminConsoleWithSubmenu->render( $output, $i18n->get('listSubscriptionCodes title') ); +} + + +#------------------------------------------------------------------- + +=head2 wwww_purchaseSubscription + +Add this subscription to the cart. + +=cut + +sub www_purchaseSubscription { + my $self = shift; + if ($self->canView) { + $self->{_hasAddedToCart} = 1; + $self->addToCart({price => $self->getPrice}); + } + return $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_redeemSubscriptionCode ( ) + +Redeems a subscription code or returns an error. + +=cut + +sub www_redeemSubscriptionCode { + my $self = shift; + my $session = $self->session; + my $i18n = WebGUI::International->new($session, "Asset_Subscription"); + my $code = $session->form->process("code"); + my $var = {}; + + if ($code) { + my $error = $self->redeemCode( $code ); + + my $codeProperties = $self->getCode( $code ); + $var->{ batchDescription } = $codeProperties->{ description }; + $var->{ message } = $error || $i18n->get('redeem code success'); + } else { + $var->{ message } = $i18n->get('redeem code ask for code'); + } + + my $f = WebGUI::HTMLForm->new( $session ); + $f->hidden( + -name => 'func', + -value => 'redeemSubscriptionCode' + ); + $f->text( + -name => 'code', + -label => $i18n->get('code'), + -hoverHelp => $i18n->get('code description'), + -maxLength => 64, + -size => 30 + ); + $f->submit; + $var->{ codeForm } = $f->print; + + return $self->processStyle($self->processTemplate($var, 'PBtmpl0000000000000053')); +} + +1; + diff --git a/lib/WebGUI/Shop/Pay.pm b/lib/WebGUI/Shop/Pay.pm index b5d98f66d..801b4f48a 100644 --- a/lib/WebGUI/Shop/Pay.pm +++ b/lib/WebGUI/Shop/Pay.pm @@ -166,6 +166,38 @@ sub getPaymentGateways { return \@drivers; } +#------------------------------------------------------------------- + +=head2 getRecurringPeriodValues ( period ) + +A utility method that returns the internationalized name for period. + +=head3 period + +The period you want the name for. + +=cut + +sub getRecurringPeriodValues { + my $self = shift; + my $session = $self->session; + + my $i18n = WebGUI::International->new($session, 'Commerce'); + tie my %periods, "Tie::IxHash"; + %periods = ( + Weekly => $i18n->get('weekly'), + BiWeekly => $i18n->get('biweekly'), + FourWeekly => $i18n->get('fourweekly'), + Monthly => $i18n->get('monthly'), + Quarterly => $i18n->get('quarterly'), + HalfYearly => $i18n->get('halfyearly'), + Yearly => $i18n->get('yearly'), + ); + + return \%periods; +} + + #------------------------------------------------------------------- =head2 new ( $session ) diff --git a/lib/WebGUI/i18n/English/Asset_Subscription.pm b/lib/WebGUI/i18n/English/Asset_Subscription.pm new file mode 100755 index 000000000..d45099f79 --- /dev/null +++ b/lib/WebGUI/i18n/English/Asset_Subscription.pm @@ -0,0 +1,417 @@ +package WebGUI::i18n::English::Asset_Subscription; +use strict; + +our $I18N = { + 'assetName' => { + message => q|Subscription|, + lastUpdated => 0, + context => q|The name of the subscription asset|, + }, + + 'expire subscription codes' => { + message => q|Expire Subscription Codes|, + lastUpdated => 0, + context => q|the title of the expire subscription codes workflow activity| + }, + + 'no subscription code batches' => { + message => q|No subscription code batches have been created yet. Use the submenu on the right to generate a batch.|, + lastUpdated => 1101228391, + context => q|Displayed if no subscription code batches have been created| + }, + + 'listSubscriptionCodes title' => { + message => q|Manage Subscription Codes|, + lastUpdated => 1101228391, + context => q|Title of listSubscriptionCodes.| + }, + + 'batch id' => { + message => q|BatchId|, + lastUpdated => 1101228391, + context => q|Shows up in the table header in listSubscriptionCodes.| + }, + + 'subscription description' => { + message => q|Description|, + lastUpdated => 1101228391, + context => q|Form label in editSubscription| + }, + + 'manage codes' => { + message => q|Manage subscription codes|, + lastUpdated => 1101228391, + context => q|A submenu option in the Subscriptions Admin Console menu.| + }, + + 'delete subscription confirm' => { + message => q|Are you sure to delete this subscription?|, + lastUpdated => 1101754598, + context => q|Confirmation question when deleting a subscription.| + }, + + 'subscriptionId' => { + message => q|Subscription Id|, + lastUpdated => 1101228391, + context => q|Just leave it Subscription Id.| + }, + + 'generate batch' => { + message => q|Generate a batch of subscription codes|, + lastUpdated => 1101228391, + context => q|A submenu option in the Subscriptions Admin Console menu.| + }, + + 'subscription name' => { + message => q|Subscription name|, + lastUpdated => 1101228391, + context => q|Form label in editSubscription| + }, + + 'code' => { + message => q|Code|, + lastUpdated => 1101228391, + context => q|Shows up in the table header in listSubscriptionCodes.| + }, + + 'code description' => { + message => q|The subscription code that you want to redeem|, + lastUpdated => 1101228391, + }, + + 'delete batch confirm' => { + message => q|Are you sure to delete this batch?|, + lastUpdated => 1101228391, + context => q|Confirmation question when deleting a code batch.| + }, + + 'selection used' => { + message => q|date of usage between|, + lastUpdated => 1101228391, + context => q|Shows up in the selection part of listSubscriptionCodes.| + }, + + 'batch description' => { + message => q|Batch description|, + lastUpdated => 1101228391, + context => q|Form option in createSubscriptionCodeBatch.| + }, + + 'redeem code' => { + message => q|Redeem a subscription code.|, + lastUpdated => 1101228391, + context => q|The title of the URL in displayLogin that points to code redemption.| + }, + + 'selection message' => { + message => q|You can make a selection of codes by:|, + lastUpdated => 1101228391, + context => q|Shows up in the selection part of listSubscriptionCodes.| + }, + + 'subscription name description' => { + message => q|

Name of the subscription.

|, + lastUpdated => 1120861450, + }, + + 'subscription price description' => { + message => q|

Price to pay for the subscription.

|, + lastUpdated => 1120861450, + }, + + 'useSalesTax' => { + message => q|Use Sales Tax?|, + lastUpdated => 1159845025, + }, + + 'useSalesTax description' => { + message => q|Should this subscription have sales tax applied to it?|, + lastUpdated => 1159845045, + }, + + 'subscription description description' => { + message => q|

Detailed description of the subscription.

|, + lastUpdated => 1120861450, + }, + + 'subscription group description' => { + message => q|

When a user paid the fee, he/she will be added to this group.

|, + lastUpdated => 1167190387, + }, + + 'subscription duration description' => { + message => q|

This sets the length of one subscription term. ie. You pay every month, or every half year.

|, + lastUpdated => 1120861450, + }, + + 'execute on subscription description' => { + message => q|

A (Perl) script to call when someone has subscribed and paid.

|, + lastUpdated => 1167190394, + }, + + 'subscription karma description' => { + message => q|

The amount of karma which is added to the user after he/she subscribes.

|, + lastUpdated => 1120861450, + }, + + 'codes expire' => { + message => q|Codes expire after|, + lastUpdated => 1101228392, + context => q|Form option in createSubscriptionCodeBatch.| + }, + + 'no association error' => { + message => q|You have to associate this batch to at least one subscription.|, + lastUpdated => 1101228391, + context => q|An error that cab occur when creating a code batch.| + }, + + 'subscription duration' => { + message => q|Subscription period|, + lastUpdated => 1101228391, + context => q|Form label in editSubscription| + }, + + 'creation date' => { + message => q|Creation date|, + lastUpdated => 1101228391, + context => q|Shows up in the table header in listSubscriptionCodes.| + }, + + 'and' => { + message => q|and|, + lastUpdated => 1101228391, + context => q|Shows up in the selection part of listSubscriptionCodes.| + }, + + 'subscription group' => { + message => q|Subscribe to group|, + lastUpdated => 1101228391, + context => q|Form label in editSubscription| + }, + + 'manage subscriptions' => { + message => q|Subscriptions (beta)|, + lastUpdated => 1101228391, + context => q|A submenu option in the Subscriptions Admin Console menu.| + }, + + 'execute on subscription' => { + message => q|Execute on subscription|, + lastUpdated => 1101228391, + context => q|Form label in editSubscription| + }, + + 'status' => { + message => q|Status|, + lastUpdated => 1101228391, + context => q|Shows up in the table header in listSubscriptionCodes.| + }, + + 'noc' => { + message => q|Number of codes in batch|, + lastUpdated => 1101228391, + context => q|Form option in createSubscriptionCodeBatch.| + }, + + 'selection created' => { + message => q|date of creation between|, + lastUpdated => 1101228391, + context => q|Shows up in the selection part of listSubscriptionCodes.| + }, + + 'manage batches' => { + message => q|Manage subscription code batches|, + lastUpdated => 1101228391, + context => q|A submenu option in the Subscriptions Admin Console menu.| + }, + + 'association' => { + message => q|Associate with subscription|, + lastUpdated => 1101228391, + context => q|Form option in createSubscriptionCodeBatch.| + }, + + 'no description error' => { + message => q|You must enter a description.|, + lastUpdated => 1101228391, + context => q|An error that cab occur when creating a code batch.| + }, + + 'subscription price' => { + message => q|Price|, + lastUpdated => 1101228391, + context => q|Form label in editSubscription| + }, + + 'dateUsed' => { + message => q|Date of usage|, + lastUpdated => 1101228391, + context => q|Shows up in the table header in listSubscriptionCodes.| + }, + + 'create batch error' => { + message => q|An error has occurred:|, + lastUpdated => 1101754822, + context => q|Identifies an error in createSubscriptionCodeBatch.| + }, + + 'select' => { + message => q|Show selection|, + lastUpdated => 1101228391, + context => q|Shows up in the selection part of listSubscriptionCodes.| + }, + + 'edit subscription title' => { + message => q|Edit Subscription|, + lastUpdated => 1101228391, + context => q|Form label in editSubscription| + }, + + 'add subscription' => { + message => q|Add a new subscription|, + lastUpdated => 1101228391, + context => q|A submenu option in the Subscriptions Admin Console menu.| + }, + + 'list codes in batch' => { + message => q|List the codes in this batch|, + lastUpdated => 1101228391, + context => q|In listSubscriptionCodeBatches| + }, + + 'delete codes' => { + message => q|Delete selected codes|, + lastUpdated => 1101228391, + context => q|Shows up in listSubscriptionCodes.| + }, + + 'subscription karma' => { + message => q|Karma|, + lastUpdated => 1101228391, + context => q|Form label in editSubscription| + }, + + 'create batch menu' => { + message => q|Create a batch of subscription codes|, + lastUpdated => 1101228391, + context => q|Menu name for createSubscriptionCodeBatch.| + }, + + 'noc description' => { + message => q|

Number of codes to create

|, + lastUpdated => 1120858265, + }, + + 'code length description' => { + message => q|

The number of characters in the generated codes. Codes must be at least 10 +characters long.

|, + lastUpdated => 1120858265, + }, + + 'codes expire description' => { + message => q|

The code must be used before this date.

|, + lastUpdated => 1132353871, + }, + + 'association description' => { + message => q|

Which subscription(s) are made with the generated codes.

|, + lastUpdated => 1120858265, + }, + + 'batch description description' => { + message => q|Description of the batch.|, + lastUpdated => 1120858265, + }, + + 'no subscriptions' => { + message => q|There are no subscriptions yet. You can add subscriptions by using the 'Add Subscription' option in the menu on the right of the screen.|, + lastUpdated => 0, + context => q|A message that shows up in manage subscriptions indicating that there are no subscriptions at all.| + }, + + 'redeem code success' => { + message => q|You've successfully subscribed to the subscriptions. You can enter another code below.|, + lastUpdated => 0, + context => q|The success message for the code redemption function.| + }, + 'redeem code failure' => { + message => q|You've entered a code that's wrong, already being used or expired. Please enter another code below.|, + lastUpdated => 1101754837, + context => q|The failure message for the code redemption function.| + }, + 'redeem code ask for code' => { + message => q|Please enter your subscription code below.|, + lastUpdated => 0, + context => q|The enter a code message for the code redemption function.| + }, + 'selection batch id' => { + message => q|batch ID|, + lastUpdated => 0, + context => q|Shows up in the selection part of listSubscriptionCodes.| + }, + + 'help redeem code template body' => { + message => q|The following template variables are available through this template:
+
+batchDescription
+The description of the batch tied to the subscription code.
+
+message
+The message that gives the result of your action. Depending on what you've done it says that you can enter a code, you've entered the wrong code, or you've successfully redeemed your code.
+
+codeForm +The form in which the user can enter his subscription code.
|, + lastUpdated => 1146593261, + context => q|The body of the help page of the code redemption template.| + }, + + 'help redeem code template title' => { + message => q|Redeem subscription code template|, + lastUpdated => 1101754848, + context => q|The title of the help page of the code redemption template.| + }, + + 'code length' => { + message => q|Subscription code length|, + lastUpdated => 1102660410, + context => q|The label of the form field in which the length of a subscription code is entered.| + }, + 'code length error' => { + message => q|You must enter a subscription code length between 10 and 64 (border values included).|, + lastUpdated => 0, + context => q|The error message that shows up when a wrong code length is specified.| + }, + + 'topicName' => { + message => q|Subscriptions|, + lastUpdated => 1128920064, + }, + + 'purchase button' => { + message => q|Add to cart|, + lastUpdates => 0, + context => q|The label on the add to cart button|, + }, + + 'default thank you message' => { + message => q|The subsciption has been added to the cart.|, + lastUpdated => 0, + context => q|the default message that will go in the thank you message field|, + }, + + 'thank you message' => { + message => q|Thank You Message|, + lastUpdated => 0, + context => q|the label for the field where you type in teh message shown when a subscription is purchased|, + }, + + 'thank you message help' => { + message => q|Use this field to define the message that informs users that they've just put a subscription into the cart. Please note that the subscription will not be applied until the user checks out.|, + lastUpdated => 0, + context => q|help for the thank you message field|, + }, + +}; + +1;