├── commerce_examples.commerce_adjustment_types.yml ├── config └── install │ ├── commerce_product.commerce_product_attribute.size.yml │ ├── commerce_product.commerce_product_attribute.color.yml │ ├── commerce_product.commerce_product_variation_type.ebook.yml │ ├── commerce_product.commerce_product_type.ebook.yml │ ├── commerce_product.commerce_product_type.t_shirt.yml │ ├── commerce_product.commerce_product_variation_type.t_shirt.yml │ ├── commerce_order.commerce_order_type.digital.yml │ ├── commerce_order.commerce_order_type.b2b.yml │ ├── commerce_payment.commerce_payment_gateway.example_on_site.yml │ ├── field.storage.commerce_product_variation.weight.yml │ ├── field.storage.commerce_product_variation.attribute_color.yml │ ├── field.storage.commerce_product_variation.attribute_size.yml │ ├── field.field.commerce_product.ebook.body.yml │ ├── field.field.commerce_product.t_shirt.body.yml │ ├── field.field.commerce_product.ebook.stores.yml │ ├── field.field.commerce_product.t_shirt.stores.yml │ ├── field.field.commerce_order.digital.order_items.yml │ ├── field.field.commerce_product_variation.t_shirt.weight.yml │ ├── core.entity_view_display.commerce_product_attribute_value.color.default.yml │ ├── field.field.commerce_product.ebook.variations.yml │ ├── field.field.commerce_product.t_shirt.variations.yml │ ├── field.storage.commerce_product_attribute_value.field_swatch.yml │ ├── field.field.commerce_product_variation.t_shirt.attribute_size.yml │ ├── field.field.commerce_product_variation.t_shirt.attribute_color.yml │ ├── core.entity_view_display.commerce_product.ebook.default.yml │ ├── core.entity_view_display.commerce_product.t_shirt.default.yml │ ├── core.entity_view_display.commerce_product_variation.ebook.default.yml │ ├── field.field.commerce_product_attribute_value.color.field_swatch.yml │ ├── core.entity_view_display.commerce_product_variation.t_shirt.default.yml │ ├── core.entity_form_display.commerce_order.digital.default.yml │ ├── core.entity_form_display.commerce_product_variation.t_shirt.default.yml │ ├── core.entity_form_display.commerce_product.t_shirt.default.yml │ └── user.role.store_admin.yml ├── data ├── demo_ebooks.csv ├── demo_t_shirts.csv └── fillerama.json ├── migrations ├── commerce_examples_product_attribute_size.yml ├── commerce_examples_product_attribute_color.yml ├── commerce_examples_product_variation_import_ebook.yml ├── commerce_examples_product_import_ebook.yml ├── commerce_examples_product_import_tshirt.yml └── commerce_examples_product_variation_import_tshirt.yml ├── composer.json ├── commerce_examples.module ├── src ├── Plugin │ ├── migrate │ │ ├── source │ │ │ ├── Product.php │ │ │ ├── ProductAttribute.php │ │ │ └── CSV.php │ │ └── process │ │ │ └── Fillerama.php │ ├── Field │ │ └── FieldFormatter │ │ │ └── ColorNameFormatter.php │ └── Commerce │ │ └── ShippingMethod │ │ └── Table.php ├── Resolvers │ ├── DemoStoreResolver.php │ ├── UrlQueryPriceResolver.php │ ├── ProductTypeOrderTypeResolver.php │ └── RolesOrderTypeResolver.php ├── RevertDemo.php ├── Demo │ ├── DemoCsv.php │ ├── MigrationRunner.php │ └── CSVFileObject.php ├── OrderProcessor │ └── ApplyTaxAdjustments.php └── GenerateOrder.php ├── templates └── commerce-product--full.html.twig ├── README.md ├── commerce_examples.services.yml ├── commerce_examples.install ├── commerce_examples.info.yml ├── tests └── src │ └── Kernel │ ├── DemoInstalledTest.php │ └── RunMigrationTest.php └── .travis.yml /commerce_examples.commerce_adjustment_types.yml: -------------------------------------------------------------------------------- 1 | tax: 2 | label: VAT 3 | has_ui: true 4 | weight: 100 -------------------------------------------------------------------------------- /config/install/commerce_product.commerce_product_attribute.size.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: { } 4 | id: size 5 | label: Size 6 | elementType: select 7 | -------------------------------------------------------------------------------- /config/install/commerce_product.commerce_product_attribute.color.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: { } 4 | id: color 5 | label: Color 6 | elementType: commerce_product_rendered_attribute 7 | -------------------------------------------------------------------------------- /config/install/commerce_product.commerce_product_variation_type.ebook.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: { } 4 | id: ebook 5 | label: eBook 6 | orderItemType: default 7 | generateTitle: true 8 | traits: { } 9 | -------------------------------------------------------------------------------- /config/install/commerce_product.commerce_product_type.ebook.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: { } 4 | id: ebook 5 | label: eBook 6 | description: '' 7 | variationType: ebook 8 | injectVariationFields: true 9 | traits: { } 10 | -------------------------------------------------------------------------------- /config/install/commerce_product.commerce_product_type.t_shirt.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: { } 4 | id: t_shirt 5 | label: T-Shirt 6 | description: '' 7 | variationType: t_shirt 8 | injectVariationFields: true 9 | traits: { } 10 | -------------------------------------------------------------------------------- /config/install/commerce_product.commerce_product_variation_type.t_shirt.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: { } 4 | id: t_shirt 5 | label: T-Shirt 6 | orderItemType: default 7 | generateTitle: true 8 | traits: 9 | - purchasable_entity_shippable 10 | -------------------------------------------------------------------------------- /config/install/commerce_order.commerce_order_type.digital.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: { } 4 | label: Digital 5 | id: digital 6 | workflow: order_default 7 | traits: { } 8 | refresh_mode: always 9 | refresh_frequency: 30 10 | sendReceipt: true 11 | receiptBcc: '' 12 | -------------------------------------------------------------------------------- /config/install/commerce_order.commerce_order_type.b2b.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: { } 4 | label: 'Business to Business' 5 | id: b2b 6 | workflow: order_default 7 | traits: { } 8 | refresh_mode: always 9 | refresh_frequency: 30 10 | sendReceipt: true 11 | receiptBcc: '' 12 | -------------------------------------------------------------------------------- /data/demo_ebooks.csv: -------------------------------------------------------------------------------- 1 | Name,SKU,Price,Description 2 | Drupal 8 Development Cookbook,AAABBCCCDDD,49.00,One of first Drupal 8 books. 3 | Learning Drupal Commerce,EEFFGGHHII,15.00,"Learn how to Drupal Commerce, the right way." 4 | "Bojan, the King of Pancevo",LKDIODLKD,100.00,Learn about King Bojan of Pancevo 5 | -------------------------------------------------------------------------------- /config/install/commerce_payment.commerce_payment_gateway.example_on_site.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | module: 5 | - commerce_payment_example 6 | id: example_on_site 7 | label: 'Example On-site' 8 | weight: null 9 | plugin: example_onsite 10 | configuration: 11 | api_key: fsdfdsfsdsdf 12 | mode: test 13 | payment_method_types: 14 | - credit_card 15 | -------------------------------------------------------------------------------- /config/install/field.storage.commerce_product_variation.weight.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | module: 5 | - commerce_product 6 | - physical 7 | id: commerce_product_variation.weight 8 | field_name: weight 9 | entity_type: commerce_product_variation 10 | type: physical_measurement 11 | settings: 12 | measurement_type: weight 13 | module: physical 14 | locked: true 15 | cardinality: 1 16 | translatable: false 17 | indexes: { } 18 | persist_with_no_fields: false 19 | custom_storage: false 20 | -------------------------------------------------------------------------------- /migrations/commerce_examples_product_attribute_size.yml: -------------------------------------------------------------------------------- 1 | id: commerce_examples_product_attribute_size 2 | status: true 3 | migration_tags: 4 | - commerce_examples 5 | source: 6 | plugin: commerce_examples_csv_attribute_values 7 | path: data/demo_t_shirts.csv 8 | header_row_count: 1 9 | keys: 10 | - Size 11 | process: 12 | name: Size 13 | attribute: 14 | plugin: default_value 15 | default_value: size 16 | destination: 17 | plugin: 'entity:commerce_product_attribute_value' 18 | migration_dependencies: { } 19 | -------------------------------------------------------------------------------- /migrations/commerce_examples_product_attribute_color.yml: -------------------------------------------------------------------------------- 1 | id: commerce_examples_product_attribute_color 2 | status: true 3 | migration_tags: 4 | - commerce_examples 5 | source: 6 | plugin: commerce_examples_csv_attribute_values 7 | path: data/demo_t_shirts.csv 8 | header_row_count: 1 9 | keys: 10 | - Color 11 | process: 12 | name: Color 13 | attribute: 14 | plugin: default_value 15 | default_value: color 16 | destination: 17 | plugin: 'entity:commerce_product_attribute_value' 18 | migration_dependencies: { } 19 | -------------------------------------------------------------------------------- /config/install/field.storage.commerce_product_variation.attribute_color.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | module: 5 | - commerce_product 6 | id: commerce_product_variation.attribute_color 7 | field_name: attribute_color 8 | entity_type: commerce_product_variation 9 | type: entity_reference 10 | settings: 11 | target_type: commerce_product_attribute_value 12 | module: core 13 | locked: false 14 | cardinality: 1 15 | translatable: false 16 | indexes: { } 17 | persist_with_no_fields: false 18 | custom_storage: false 19 | -------------------------------------------------------------------------------- /config/install/field.storage.commerce_product_variation.attribute_size.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | module: 5 | - commerce_product 6 | id: commerce_product_variation.attribute_size 7 | field_name: attribute_size 8 | entity_type: commerce_product_variation 9 | type: entity_reference 10 | settings: 11 | target_type: commerce_product_attribute_value 12 | module: core 13 | locked: false 14 | cardinality: 1 15 | translatable: false 16 | indexes: { } 17 | persist_with_no_fields: false 18 | custom_storage: false 19 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product.ebook.body.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_type.ebook 6 | - field.storage.commerce_product.body 7 | module: 8 | - text 9 | id: commerce_product.ebook.body 10 | field_name: body 11 | entity_type: commerce_product 12 | bundle: ebook 13 | label: Body 14 | description: '' 15 | required: false 16 | translatable: true 17 | default_value: { } 18 | default_value_callback: '' 19 | settings: 20 | display_summary: false 21 | field_type: text_with_summary 22 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product.t_shirt.body.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_type.t_shirt 6 | - field.storage.commerce_product.body 7 | module: 8 | - text 9 | id: commerce_product.t_shirt.body 10 | field_name: body 11 | entity_type: commerce_product 12 | bundle: t_shirt 13 | label: Body 14 | description: '' 15 | required: false 16 | translatable: true 17 | default_value: { } 18 | default_value_callback: '' 19 | settings: 20 | display_summary: false 21 | field_type: text_with_summary 22 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product.ebook.stores.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_type.ebook 6 | - field.storage.commerce_product.stores 7 | id: commerce_product.ebook.stores 8 | field_name: stores 9 | entity_type: commerce_product 10 | bundle: ebook 11 | label: Stores 12 | description: '' 13 | required: true 14 | translatable: false 15 | default_value: { } 16 | default_value_callback: '' 17 | settings: 18 | handler: 'default:commerce_store' 19 | handler_settings: { } 20 | field_type: entity_reference 21 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product.t_shirt.stores.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_type.t_shirt 6 | - field.storage.commerce_product.stores 7 | id: commerce_product.t_shirt.stores 8 | field_name: stores 9 | entity_type: commerce_product 10 | bundle: t_shirt 11 | label: Stores 12 | description: '' 13 | required: true 14 | translatable: false 15 | default_value: { } 16 | default_value_callback: '' 17 | settings: 18 | handler: 'default:commerce_store' 19 | handler_settings: { } 20 | field_type: entity_reference 21 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_order.digital.order_items.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_order.commerce_order_type.digital 6 | - field.storage.commerce_order.order_items 7 | id: commerce_order.digital.order_items 8 | field_name: order_items 9 | entity_type: commerce_order 10 | bundle: digital 11 | label: 'Order items' 12 | description: '' 13 | required: true 14 | translatable: false 15 | default_value: { } 16 | default_value_callback: null 17 | settings: 18 | handler: 'default:commerce_order_item' 19 | handler_settings: { } 20 | field_type: entity_reference 21 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product_variation.t_shirt.weight.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_variation_type.t_shirt 6 | - field.storage.commerce_product_variation.weight 7 | module: 8 | - physical 9 | id: commerce_product_variation.t_shirt.weight 10 | field_name: weight 11 | entity_type: commerce_product_variation 12 | bundle: t_shirt 13 | label: Weight 14 | description: '' 15 | required: true 16 | translatable: false 17 | default_value: { } 18 | default_value_callback: null 19 | settings: { } 20 | field_type: physical_measurement 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "drupal/commerce_examples", 3 | "type": "drupal-module", 4 | "description": "Commerce demo stuff", 5 | "keywords": ["Drupal"], 6 | "license": "GPL-2.0+", 7 | "homepage": "https://www.drupal.org/project/commerce_examples", 8 | "minimum-stability": "dev", 9 | "support": { 10 | "issues": "http://drupal.org/project/issues/commerce_examples", 11 | "source": "http://cgit.drupalcode.org/commerce_examples" 12 | }, 13 | "require": { 14 | "drupal/commerce": "^2", 15 | "drupal/commerce_shipping": "^2" 16 | }, 17 | "require-dev": { 18 | "drupal/config_devel": "~1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /migrations/commerce_examples_product_variation_import_ebook.yml: -------------------------------------------------------------------------------- 1 | id: commerce_examples_product_variation_import_ebook 2 | status: true 3 | migration_tags: 4 | - commerce_examples 5 | source: 6 | plugin: csv 7 | path: data/demo_ebooks.csv 8 | header_row_count: 1 9 | keys: 10 | - SKU 11 | process: 12 | title: 13 | plugin: default_value 14 | default_value: null 15 | sku: SKU 16 | type: 17 | plugin: default_value 18 | default_value: ebook 19 | price/number: Price 20 | price/currency_code: 21 | plugin: default_value 22 | default_value: USD 23 | destination: 24 | plugin: 'entity:commerce_product_variation' 25 | migration_dependencies: { } 26 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.commerce_product_attribute_value.color.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_attribute.color 6 | - field.field.commerce_product_attribute_value.color.field_swatch 7 | module: 8 | - commerce_examples 9 | id: commerce_product_attribute_value.color.default 10 | targetEntityType: commerce_product_attribute_value 11 | bundle: color 12 | mode: default 13 | content: 14 | name: 15 | label: hidden 16 | type: commerce_examples_color_name 17 | weight: -5 18 | region: content 19 | settings: { } 20 | third_party_settings: { } 21 | hidden: 22 | field_swatch: true 23 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product.ebook.variations.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_type.ebook 6 | - commerce_product.commerce_product_variation_type.ebook 7 | - field.storage.commerce_product.variations 8 | id: commerce_product.ebook.variations 9 | field_name: variations 10 | entity_type: commerce_product 11 | bundle: ebook 12 | label: Variations 13 | description: '' 14 | required: true 15 | translatable: false 16 | default_value: { } 17 | default_value_callback: '' 18 | settings: 19 | handler: 'default:commerce_product_variation' 20 | handler_settings: 21 | target_bundles: 22 | - ebook 23 | field_type: entity_reference 24 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product.t_shirt.variations.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_type.t_shirt 6 | - commerce_product.commerce_product_variation_type.t_shirt 7 | - field.storage.commerce_product.variations 8 | id: commerce_product.t_shirt.variations 9 | field_name: variations 10 | entity_type: commerce_product 11 | bundle: t_shirt 12 | label: Variations 13 | description: '' 14 | required: true 15 | translatable: false 16 | default_value: { } 17 | default_value_callback: '' 18 | settings: 19 | handler: 'default:commerce_product_variation' 20 | handler_settings: 21 | target_bundles: 22 | - t_shirt 23 | field_type: entity_reference 24 | -------------------------------------------------------------------------------- /config/install/field.storage.commerce_product_attribute_value.field_swatch.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | module: 5 | - commerce_product 6 | - file 7 | - image 8 | id: commerce_product_attribute_value.field_swatch 9 | field_name: field_swatch 10 | entity_type: commerce_product_attribute_value 11 | type: image 12 | settings: 13 | uri_scheme: public 14 | default_image: 15 | uuid: '' 16 | alt: '' 17 | title: '' 18 | width: null 19 | height: null 20 | target_type: file 21 | display_field: false 22 | display_default: false 23 | module: image 24 | locked: false 25 | cardinality: 1 26 | translatable: true 27 | indexes: { } 28 | persist_with_no_fields: false 29 | custom_storage: false 30 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product_variation.t_shirt.attribute_size.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_attribute.size 6 | - commerce_product.commerce_product_variation_type.t_shirt 7 | - field.storage.commerce_product_variation.attribute_size 8 | id: commerce_product_variation.t_shirt.attribute_size 9 | field_name: attribute_size 10 | entity_type: commerce_product_variation 11 | bundle: t_shirt 12 | label: Size 13 | description: '' 14 | required: true 15 | translatable: false 16 | default_value: { } 17 | default_value_callback: '' 18 | settings: 19 | handler: 'default:commerce_product_attribute_value' 20 | handler_settings: 21 | target_bundles: 22 | - size 23 | field_type: entity_reference 24 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product_variation.t_shirt.attribute_color.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_attribute.color 6 | - commerce_product.commerce_product_variation_type.t_shirt 7 | - field.storage.commerce_product_variation.attribute_color 8 | id: commerce_product_variation.t_shirt.attribute_color 9 | field_name: attribute_color 10 | entity_type: commerce_product_variation 11 | bundle: t_shirt 12 | label: Color 13 | description: '' 14 | required: true 15 | translatable: false 16 | default_value: { } 17 | default_value_callback: '' 18 | settings: 19 | handler: 'default:commerce_product_attribute_value' 20 | handler_settings: 21 | target_bundles: 22 | - color 23 | field_type: entity_reference 24 | -------------------------------------------------------------------------------- /commerce_examples.module: -------------------------------------------------------------------------------- 1 | [ 10 | 'type' => 'sequence', 11 | 'label' => 'Extra keys', 12 | 'sequence' => [ 13 | 'type' => 'string', 14 | 'label' => 'Key', 15 | ], 16 | ], 17 | ]; 18 | } 19 | } 20 | 21 | /** 22 | * Implements hook_theme(). 23 | */ 24 | function commerce_examples_theme($existing, $type, $theme, $path) { 25 | return [ 26 | 'commerce_product__full' => [ 27 | 'render element' => 'elements', 28 | 'base hook' => 'commerce_product', 29 | ], 30 | ]; 31 | } 32 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.commerce_product.ebook.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_type.ebook 6 | - field.field.commerce_product.ebook.body 7 | - field.field.commerce_product.ebook.stores 8 | - field.field.commerce_product.ebook.variations 9 | module: 10 | - commerce_product 11 | - text 12 | id: commerce_product.ebook.default 13 | targetEntityType: commerce_product 14 | bundle: ebook 15 | mode: default 16 | content: 17 | body: 18 | label: hidden 19 | type: text_default 20 | weight: -4 21 | settings: { } 22 | third_party_settings: { } 23 | region: content 24 | variations: 25 | type: commerce_add_to_cart 26 | weight: 0 27 | label: above 28 | settings: 29 | combine: true 30 | third_party_settings: { } 31 | region: content 32 | hidden: 33 | created: true 34 | stores: true 35 | uid: true 36 | -------------------------------------------------------------------------------- /migrations/commerce_examples_product_import_ebook.yml: -------------------------------------------------------------------------------- 1 | id: commerce_examples_product_import_ebook 2 | status: true 3 | migration_tags: 4 | - commerce_examples 5 | source: 6 | plugin: commerce_examples_product_csv 7 | path: data/demo_ebooks.csv 8 | file_class: Drupal\commerce_examples\Demo\DemoCsv 9 | header_row_count: 1 10 | keys: 11 | - Name 12 | process: 13 | title: Name 14 | type: 15 | plugin: default_value 16 | default_value: ebook 17 | stores: 18 | plugin: default_value 19 | default_value: 20 | - 1 21 | variations/target_id: 22 | - 23 | plugin: migration 24 | migration: commerce_examples_product_variation_import_ebook 25 | source: product_variations 26 | - 27 | plugin: skip_on_empty 28 | method: row 29 | body: 30 | plugin: fillerama 31 | destination: 32 | plugin: 'entity:commerce_product' 33 | migration_dependencies: 34 | required: 35 | - commerce_examples_product_variation_import_ebook 36 | -------------------------------------------------------------------------------- /src/Plugin/migrate/source/Product.php: -------------------------------------------------------------------------------- 1 | getSourceProperty('SKU'), 0, 9); 27 | 28 | $query = \Drupal::entityQuery('commerce_product_variation') 29 | ->condition('sku', $sku_prefix, 'STARTS_WITH'); 30 | 31 | $values = $query->execute(); 32 | 33 | foreach ($values as $value) { 34 | $targets[] = ['target_id' => $value]; 35 | } 36 | 37 | $row->setDestinationProperty('variations', $targets); 38 | $row->rehash(); 39 | return TRUE; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.commerce_product.t_shirt.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_type.t_shirt 6 | - field.field.commerce_product.t_shirt.body 7 | - field.field.commerce_product.t_shirt.stores 8 | - field.field.commerce_product.t_shirt.variations 9 | module: 10 | - commerce_product 11 | - text 12 | id: commerce_product.t_shirt.default 13 | targetEntityType: commerce_product 14 | bundle: t_shirt 15 | mode: default 16 | content: 17 | body: 18 | label: hidden 19 | type: text_default 20 | weight: -4 21 | settings: { } 22 | third_party_settings: { } 23 | region: content 24 | variations: 25 | type: commerce_add_to_cart 26 | weight: 0 27 | label: hidden 28 | settings: 29 | show_quantity: false 30 | default_quantity: '1' 31 | combine: true 32 | third_party_settings: { } 33 | region: content 34 | hidden: 35 | created: true 36 | stores: true 37 | uid: true 38 | -------------------------------------------------------------------------------- /templates/commerce-product--full.html.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * 5 | * Default product template. 6 | * 7 | * Available variables: 8 | * - attributes: HTML attributes for the wrapper. 9 | * - product: The rendered product fields. 10 | * Use 'product' to print them all, or print a subset such as 11 | * 'product.title'. Use the following code to exclude the 12 | * printing of a given field: 13 | * @code 14 | * {{ product|without('title') }} 15 | * @endcode 16 | * - product_entity: The product entity. 17 | * - product_url: The product URL. 18 | * 19 | * @ingroup themeable 20 | */ 21 | #} 22 | 23 |
24 | {{- product|without('variation_attributes', 'variations', 'variation_title', 'variation_sku', 'variation_price') -}} 25 |
26 |
27 | {{ product.variation_title }} 28 | {{ product.variation_sku }} 29 | {{ product.variation_price }} 30 | {{ product.variations }} 31 |
32 | 33 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.commerce_product_variation.ebook.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_variation_type.ebook 6 | module: 7 | - commerce_price 8 | id: commerce_product_variation.ebook.default 9 | targetEntityType: commerce_product_variation 10 | bundle: ebook 11 | mode: default 12 | content: 13 | price: 14 | label: inline 15 | type: commerce_price_calculated 16 | weight: 0 17 | settings: 18 | strip_trailing_zeroes: false 19 | display_currency_code: false 20 | third_party_settings: { } 21 | region: content 22 | sku: 23 | label: hidden 24 | type: string 25 | weight: -4 26 | settings: 27 | link_to_entity: false 28 | third_party_settings: { } 29 | region: content 30 | title: 31 | label: hidden 32 | type: string 33 | weight: -5 34 | settings: 35 | link_to_entity: false 36 | third_party_settings: { } 37 | region: content 38 | hidden: 39 | product_id: true 40 | -------------------------------------------------------------------------------- /src/Plugin/migrate/source/ProductAttribute.php: -------------------------------------------------------------------------------- 1 | next(); 23 | while ($fileIterator->valid()) { 24 | $row_data = $fileIterator->current() + $this->configuration; 25 | $fileIterator->next(); 26 | 27 | $colors[] = $row_data[$this->configuration['keys'][0]]; 28 | } 29 | $items = array_unique($colors); 30 | $items = array_filter($items); 31 | 32 | foreach ($items as $item) { 33 | $all_items[] = [ 34 | $this->configuration['keys'][0] => $item, 35 | ]; 36 | } 37 | 38 | return new \ArrayIterator($all_items); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Commerce Examples [![Build Status] 2 | =============================== 3 | 4 | An examples module for Commerce 2.x. Provides some usually defaults and a sample 5 | migration. 6 | 7 | For demo, see https://github.com/bojanz/commerce_demo 8 | 9 | ## Installation 10 | 11 | Add this private repository to the composer configuration repositories list: 12 | ``` 13 | composer config repositories.mglaman vcs https://github.com/mglaman/commerce_examples 14 | ``` 15 | 16 | Then, install this module: 17 | ``` 18 | composer require drupal/commerce_examples:dev-master 19 | ``` 20 | 21 | ## Features 22 | 23 | * Sample product type 24 | * Color and size attributes 25 | * Sample product display and add to cart form configurations 26 | * Migration example from local CSV source 27 | * Price resolvers 28 | * Order type resolvers 29 | 30 | #### Price resolver 31 | 32 | Visit any product and add `?discount=TRUE` to trigger a 15% discount. 33 | 34 | #### Order type resolver 35 | 36 | Based on the product variation type, the product will go into the default order type, or be treated as digital. 37 | -------------------------------------------------------------------------------- /migrations/commerce_examples_product_import_tshirt.yml: -------------------------------------------------------------------------------- 1 | id: commerce_examples_product_import_tshirt 2 | status: true 3 | migration_tags: 4 | - commerce_examples 5 | source: 6 | plugin: commerce_examples_product_csv 7 | path: data/demo_t_shirts.csv 8 | # file_class: Drupal\commerce_examples\Demo\DemoCsv 9 | header_row_count: 1 10 | keys: 11 | - Name 12 | process: 13 | title: Name 14 | type: 15 | plugin: default_value 16 | default_value: t_shirt 17 | stores: 18 | plugin: default_value 19 | default_value: 20 | - 1 21 | variations/target_id: 22 | - 23 | plugin: migration 24 | migration: commerce_examples_product_variation_import_tshirt 25 | source: product_variations 26 | - 27 | plugin: skip_on_empty 28 | method: row 29 | body/value: 30 | plugin: fillerama 31 | 'body/format': 32 | plugin: default_value 33 | default_value: 'full_html' 34 | destination: 35 | plugin: 'entity:commerce_product' 36 | migration_dependencies: 37 | required: 38 | - commerce_examples_product_variation_import_tshirt 39 | -------------------------------------------------------------------------------- /config/install/field.field.commerce_product_attribute_value.color.field_swatch.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_attribute.color 6 | - field.storage.commerce_product_attribute_value.field_swatch 7 | module: 8 | - image 9 | id: commerce_product_attribute_value.color.field_swatch 10 | field_name: field_swatch 11 | entity_type: commerce_product_attribute_value 12 | bundle: color 13 | label: Swatch 14 | description: '' 15 | required: false 16 | translatable: false 17 | default_value: { } 18 | default_value_callback: '' 19 | settings: 20 | file_extensions: 'png gif jpg jpeg' 21 | alt_field: true 22 | alt_field_required: true 23 | title_field: false 24 | title_field_required: false 25 | max_resolution: '' 26 | min_resolution: '' 27 | default_image: 28 | uuid: null 29 | alt: '' 30 | title: '' 31 | width: null 32 | height: null 33 | file_directory: '[date:custom:Y]-[date:custom:m]' 34 | max_filesize: '' 35 | handler: 'default:file' 36 | handler_settings: { } 37 | field_type: image 38 | -------------------------------------------------------------------------------- /src/Resolvers/DemoStoreResolver.php: -------------------------------------------------------------------------------- 1 | storage = $entity_type_manager->getStorage('commerce_store'); 28 | } 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function resolve() { 34 | if (!$this->storage->loadDefault()) { 35 | $stores = $this->storage->loadMultiple(); 36 | return reset($stores); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /config/install/core.entity_view_display.commerce_product_variation.t_shirt.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_variation_type.t_shirt 6 | - field.field.commerce_product_variation.t_shirt.attribute_color 7 | - field.field.commerce_product_variation.t_shirt.attribute_size 8 | - field.field.commerce_product_variation.t_shirt.weight 9 | module: 10 | - commerce_price 11 | id: commerce_product_variation.t_shirt.default 12 | targetEntityType: commerce_product_variation 13 | bundle: t_shirt 14 | mode: default 15 | content: 16 | price: 17 | label: inline 18 | type: commerce_price_calculated 19 | weight: 0 20 | settings: 21 | strip_trailing_zeroes: false 22 | display_currency_code: false 23 | third_party_settings: { } 24 | region: content 25 | sku: 26 | label: hidden 27 | type: string 28 | weight: -4 29 | settings: 30 | link_to_entity: false 31 | third_party_settings: { } 32 | region: content 33 | title: 34 | label: hidden 35 | type: string 36 | weight: -5 37 | settings: 38 | link_to_entity: false 39 | third_party_settings: { } 40 | region: content 41 | hidden: 42 | attribute_color: true 43 | attribute_size: true 44 | product_id: true 45 | weight: true 46 | -------------------------------------------------------------------------------- /migrations/commerce_examples_product_variation_import_tshirt.yml: -------------------------------------------------------------------------------- 1 | id: commerce_examples_product_variation_import_tshirt 2 | status: true 3 | migration_tags: 4 | - commerce_examples 5 | source: 6 | plugin: csv 7 | path: data/demo_t_shirts.csv 8 | header_row_count: 1 9 | keys: 10 | - SKU 11 | process: 12 | title: 13 | plugin: default_value 14 | default_value: null 15 | sku: SKU 16 | type: 17 | plugin: default_value 18 | default_value: t_shirt 19 | price/number: Price 20 | price/currency_code: 21 | plugin: default_value 22 | default_value: USD 23 | attribute_size/target_id: 24 | - 25 | plugin: migration 26 | migration: commerce_examples_product_attribute_size 27 | source: Size 28 | attribute_color/target_id: 29 | - 30 | plugin: migration 31 | migration: commerce_examples_product_attribute_color 32 | source: Color 33 | weight/number: 34 | - 35 | plugin: default_value 36 | source: Weight 37 | default_value: oz 38 | weight/unit: 39 | - 40 | plugin: default_value 41 | source: WeightUnit 42 | default_value: oz 43 | destination: 44 | plugin: 'entity:commerce_product_variation' 45 | migration_dependencies: 46 | required: 47 | - commerce_examples_product_attribute_color 48 | - commerce_examples_product_attribute_size 49 | -------------------------------------------------------------------------------- /config/install/core.entity_form_display.commerce_order.digital.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_order.commerce_order_type.digital 6 | - field.field.commerce_order.digital.order_items 7 | module: 8 | - commerce_order 9 | - inline_entity_form 10 | id: commerce_order.digital.default 11 | targetEntityType: commerce_order 12 | bundle: digital 13 | mode: default 14 | content: 15 | adjustments: 16 | type: commerce_adjustment_default 17 | weight: 0 18 | region: content 19 | settings: { } 20 | third_party_settings: { } 21 | billing_profile: 22 | type: commerce_billing_profile 23 | weight: 0 24 | settings: { } 25 | region: content 26 | third_party_settings: { } 27 | cart: 28 | type: boolean_checkbox 29 | settings: 30 | display_label: true 31 | weight: 20 32 | region: content 33 | third_party_settings: { } 34 | order_items: 35 | type: inline_entity_form_complex 36 | weight: 0 37 | settings: 38 | override_labels: true 39 | label_singular: 'order item' 40 | label_plural: 'order items' 41 | form_mode: default 42 | allow_new: true 43 | allow_existing: false 44 | match_operator: CONTAINS 45 | third_party_settings: { } 46 | region: content 47 | hidden: 48 | ip_address: true 49 | mail: true 50 | order_number: true 51 | state: true 52 | store_id: true 53 | uid: true 54 | -------------------------------------------------------------------------------- /src/Plugin/Field/FieldFormatter/ColorNameFormatter.php: -------------------------------------------------------------------------------- 1 | $item) { 28 | $elements[$delta] = [ 29 | '#type' => 'inline_template', 30 | '#template' => '
', 31 | '#context' => [ 32 | 'value' => $item->value, 33 | 'value_style' => strtolower($item->value), 34 | ], 35 | ]; 36 | } 37 | return $elements; 38 | } 39 | 40 | /** 41 | * {@inheritdoc} 42 | */ 43 | public static function isApplicable(FieldDefinitionInterface $field_definition) { 44 | return $field_definition->getTargetEntityTypeId() == 'commerce_product_attribute_value' && $field_definition->getType() == 'string'; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /config/install/core.entity_form_display.commerce_product_variation.t_shirt.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_variation_type.t_shirt 6 | - field.field.commerce_product_variation.t_shirt.attribute_color 7 | - field.field.commerce_product_variation.t_shirt.attribute_size 8 | - field.field.commerce_product_variation.t_shirt.weight 9 | module: 10 | - commerce_price 11 | - physical 12 | id: commerce_product_variation.t_shirt.default 13 | targetEntityType: commerce_product_variation 14 | bundle: t_shirt 15 | mode: default 16 | content: 17 | attribute_color: 18 | type: options_select 19 | weight: 2 20 | settings: { } 21 | third_party_settings: { } 22 | region: content 23 | attribute_size: 24 | type: options_select 25 | weight: 3 26 | settings: { } 27 | third_party_settings: { } 28 | region: content 29 | price: 30 | type: commerce_price_default 31 | weight: 1 32 | settings: { } 33 | third_party_settings: { } 34 | region: content 35 | sku: 36 | type: string_textfield 37 | weight: 0 38 | settings: 39 | size: 60 40 | placeholder: '' 41 | third_party_settings: { } 42 | region: content 43 | weight: 44 | type: physical_measurement_default 45 | weight: 4 46 | region: content 47 | settings: 48 | default_unit: lb 49 | allow_unit_change: true 50 | third_party_settings: { } 51 | hidden: 52 | created: true 53 | status: true 54 | uid: true 55 | -------------------------------------------------------------------------------- /src/Resolvers/UrlQueryPriceResolver.php: -------------------------------------------------------------------------------- 1 | request = $request->getCurrentRequest(); 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function resolve(PurchasableEntityInterface $entity, $quantity, Context $context) { 39 | // We check if the request has a discount parameter. We will only return a 40 | // price if it does. By not returning a price, we allow other resolvers to 41 | // have a chance at returning the price value. 42 | if ($this->request->query->has('discount')) { 43 | return $entity->getPrice()->add($entity->getPrice()->multiply('-0.15')); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/RevertDemo.php: -------------------------------------------------------------------------------- 1 | readMultiple($storage->listAll('')); 24 | $dependency_manager = new ConfigDependencyManager(); 25 | $config_names = $dependency_manager->setData($data)->sortAll(); 26 | 27 | $import = []; 28 | $revert = []; 29 | foreach ($config_names as $name) { 30 | // For some reason this causes errors, skip this config. 31 | if (strpos($name, 'migrate_plus.migration_group') !== FALSE) { 32 | continue; 33 | } 34 | if ($updater->loadFromActive($name)) { 35 | $revert[] = $name; 36 | } 37 | else { 38 | $import[] = $name; 39 | } 40 | } 41 | $updater->import($import); 42 | $updater->revert($revert, FALSE); 43 | 44 | $runner = \Drupal::getContainer()->get('commerce_examples.migration_runner'); 45 | $runner->run(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/Resolvers/ProductTypeOrderTypeResolver.php: -------------------------------------------------------------------------------- 1 | orderTypeStorage = $entity_type_manager->getStorage('commerce_order_type'); 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function resolve(OrderItemInterface $order_item) { 39 | 40 | $bundle = $order_item->getPurchasedEntity()->bundle(); 41 | switch ($bundle) { 42 | case 'ebook': 43 | return $this->orderTypeStorage->load('digital')->id(); 44 | 45 | case 't_shirt': 46 | default: 47 | return $this->orderTypeStorage->load('default')->id(); 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/Resolvers/RolesOrderTypeResolver.php: -------------------------------------------------------------------------------- 1 | currentUser = $account; 41 | $this->orderTypeStorage = $entity_type_manager->getStorage('commerce_order_type'); 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function resolve(OrderItemInterface $order_item) { 48 | $user = \Drupal::currentUser(); 49 | $roles = $user->getRoles(); 50 | 51 | return in_array('b2b', $roles) ? $this->orderTypeStorage->load('b2b')->id() : NULL; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/Demo/DemoCsv.php: -------------------------------------------------------------------------------- 1 | current(); 34 | 35 | if ($this->name == '') { 36 | $this->name = $row['Name']; 37 | } 38 | while (($this->name == $row['Name'] || in_array($row['Name'], $this->importedProduct)) && !$this->eof()) { 39 | parent::next(); 40 | $row = $this->current(); 41 | } 42 | $this->name = $row['Name']; 43 | $this->importedProduct[] = $row['Name']; 44 | } 45 | 46 | /** 47 | * Returns the count of the number of items. 48 | * 49 | * For some reason, the regular SPL Filter iterator has issues, so 50 | * we're overriding this so that the counts match up. 51 | * 52 | * @return mixed 53 | * The count. 54 | */ 55 | public function count() { 56 | $this->next(); 57 | 58 | $names = []; 59 | while ($this->valid()) { 60 | $row_data = $this->current(); 61 | $this->next(); 62 | 63 | $names[] = $row_data['Name']; 64 | } 65 | $items = array_unique($names); 66 | $items = array_filter($items); 67 | 68 | return count($items); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/OrderProcessor/ApplyTaxAdjustments.php: -------------------------------------------------------------------------------- 1 | get('billing_profile')->isEmpty()) { 26 | $billing_profile = $order->getBillingProfile(); 27 | /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */ 28 | $address = $billing_profile->get('address')->first(); 29 | 30 | // Apply VAT for Germany. 31 | if ($address->getCountryCode() == 'DE') { 32 | $vat_adjustment = new Adjustment([ 33 | 'type' => 'tax', 34 | 'label' => t('VAT'), 35 | 'amount' => $order->getTotalPrice()->multiply('0.19'), 36 | ]); 37 | $order->addAdjustment($vat_adjustment); 38 | } 39 | // Let's say we have some nexus' in the United States. 40 | elseif ($address->getCountryCode() == 'US') { 41 | 42 | // Charge state sales tax for Wisconsin. 43 | if ($address->getAdministrativeArea() == 'WI') { 44 | $vat_adjustment = new Adjustment([ 45 | 'type' => 'tax', 46 | 'label' => t('Sales tax'), 47 | 'amount' => $order->getTotalPrice()->multiply('0.05'), 48 | ]); 49 | $order->addAdjustment($vat_adjustment); 50 | } 51 | } 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /data/demo_t_shirts.csv: -------------------------------------------------------------------------------- 1 | Name,SKU,Price,Size,Color,Description,Weight,WeightUnit 2 | "Commerce Guys Hoodie",15080-009-SC,89.5, Small,Cyan, "Lorem ipsum", 10,oz 3 | "Commerce Guys Hoodie",15080-009-SM,89.5, Small,Magenta, "Lorem ipsum", 10,oz 4 | "Commerce Guys Hoodie",15080-009-SY,89.5, Small,Yellow, "Lorem ipsum", 10,oz 5 | "Commerce Guys Hoodie",15080-009-SK,89.5, Small,Black, "Lorem ipsum", 10,oz 6 | "Commerce Guys Hoodie",15080-009-MC,89.5, Medium,Cyan, "Lorem ipsum", 10,oz 7 | "Commerce Guys Hoodie",15080-009-MM,89.5, Medium,Magenta, "Lorem ipsum", 10,oz 8 | "Commerce Guys Hoodie",15080-009-MY,89.5, Medium,Yellow, "Lorem ipsum", 10,oz 9 | "Commerce Guys Hoodie",15080-009-MK,89.5, Medium,Black, "Lorem ipsum", 10,oz 10 | "Commerce Guys Hoodie",15080-009-LC,89.5, Large,Cyan, "Lorem ipsum", 10,oz 11 | "Commerce Guys Hoodie",15080-009-LM,89.5, Large,Magenta, "Lorem ipsum", 10,oz 12 | "Commerce Guys Hoodie",15080-009-LY,89.5, Large,Yellow, "Lorem ipsum", 10,oz 13 | "Commerce Guys Hoodie",15080-009-LK,89.5, Large,Black, "Lorem ipsum", 10,oz 14 | "Drupal Commerce Cart Shirt",15090-009-SC,29.5, Small,Cyan, "Lorem ipsum", 7,oz 15 | "Drupal Commerce Cart Shirt",15090-009-SM,29.5, Small,Magenta, "Lorem ipsum", 7,oz 16 | "Drupal Commerce Cart Shirt",15090-009-SY,29.5, Small,Yellow, "Lorem ipsum", 7,oz 17 | "Drupal Commerce Cart Shirt",15090-009-SK,29.5, Small,Black, "Lorem ipsum", 7,oz 18 | "Drupal Commerce Cart Shirt",15090-009-MC,29.5, Medium,Cyan, "Lorem ipsum", 7,oz 19 | "Drupal Commerce Cart Shirt",15090-009-MM,29.5, Medium,Magenta, "Lorem ipsum", 7,oz 20 | "Drupal Commerce Cart Shirt",15090-009-MY,29.5, Medium,Yellow, "Lorem ipsum", 7,oz 21 | "Drupal Commerce Cart Shirt",15090-009-MK,29.5, Medium,Black, "Lorem ipsum", 7,oz 22 | "Drupal Commerce Cart Shirt",15090-009-LC,29.5, Large,Cyan, "Lorem ipsum", 7,oz 23 | "Drupal Commerce Cart Shirt",15090-009-LM,29.5, Large,Magenta, "Lorem ipsum", 7,oz 24 | "Drupal Commerce Cart Shirt",15090-009-LY,29.5, Large,Yellow, "Lorem ipsum", 7,oz 25 | "Drupal Commerce Cart Shirt",15090-009-LK,29.5, Large,Black, "Lorem ipsum", 7,oz 26 | -------------------------------------------------------------------------------- /config/install/core.entity_form_display.commerce_product.t_shirt.default.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: 4 | config: 5 | - commerce_product.commerce_product_type.t_shirt 6 | - field.field.commerce_product.t_shirt.body 7 | - field.field.commerce_product.t_shirt.stores 8 | - field.field.commerce_product.t_shirt.variations 9 | module: 10 | - commerce 11 | - inline_entity_form 12 | - path 13 | - text 14 | id: commerce_product.t_shirt.default 15 | targetEntityType: commerce_product 16 | bundle: t_shirt 17 | mode: default 18 | content: 19 | body: 20 | type: text_textarea_with_summary 21 | weight: 1 22 | settings: 23 | rows: 9 24 | summary_rows: 3 25 | placeholder: '' 26 | third_party_settings: { } 27 | region: content 28 | created: 29 | type: datetime_timestamp 30 | weight: 10 31 | settings: { } 32 | third_party_settings: { } 33 | region: content 34 | path: 35 | type: path 36 | weight: 30 37 | settings: { } 38 | third_party_settings: { } 39 | region: content 40 | stores: 41 | type: commerce_entity_select 42 | weight: -10 43 | settings: 44 | autocomplete_threshold: 7 45 | autocomplete_size: 60 46 | autocomplete_placeholder: '' 47 | third_party_settings: { } 48 | region: content 49 | title: 50 | type: string_textfield 51 | weight: -5 52 | settings: 53 | size: 60 54 | placeholder: '' 55 | third_party_settings: { } 56 | region: content 57 | uid: 58 | type: entity_reference_autocomplete 59 | weight: 5 60 | settings: 61 | match_operator: CONTAINS 62 | size: 60 63 | placeholder: '' 64 | third_party_settings: { } 65 | region: content 66 | variations: 67 | type: inline_entity_form_complex 68 | weight: 10 69 | settings: 70 | override_labels: true 71 | label_singular: variation 72 | label_plural: variations 73 | allow_new: true 74 | allow_existing: false 75 | match_operator: CONTAINS 76 | third_party_settings: { } 77 | region: content 78 | hidden: 79 | status: true 80 | -------------------------------------------------------------------------------- /commerce_examples.services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | commerce_examples.migration_runner: 3 | class: Drupal\commerce_examples\Demo\MigrationRunner 4 | arguments: ['@plugin.manager.migration'] 5 | 6 | # Demo config revert service. 7 | # @todo expose a UI to trigger this. 8 | commerce_examples.revert_demo: 9 | class: \Drupal\commerce_examples\RevertDemo 10 | # Work around for missing default store due to config WTF 11 | commerce_examples.demo_store_resolver: 12 | class: Drupal\commerce_examples\Resolvers\DemoStoreResolver 13 | arguments: ['@entity_type.manager'] 14 | tags: 15 | - { name: commerce_store.store_resolver, priority: -500 } 16 | 17 | # Resolves order type based on the product type. 18 | commerce_examples.product_type_order_type_resolver: 19 | class: Drupal\commerce_examples\Resolvers\ProductTypeOrderTypeResolver 20 | arguments: ['@entity_type.manager'] 21 | tags: 22 | - { name: commerce_order.order_type_resolver, priority: 100 } 23 | # Resolves order type based on roles. This has a priority of 200 so that it runs before product type and default. 24 | commerce_examples.roles_order_type_resolver: 25 | class: Drupal\commerce_examples\Resolvers\RolesOrderTypeResolver 26 | arguments: ['@current_user', '@entity_type.manager'] 27 | tags: 28 | - { name: commerce_order.order_type_resolver, priority: 200 } 29 | # Resolves prices for products based on a query parameter. 30 | commerce_examples.url_query_price_resolver: 31 | class: Drupal\commerce_examples\Resolvers\UrlQueryPriceResolver 32 | arguments: ['@request_stack'] 33 | tags: 34 | - { name: commerce_price.price_resolver, priority: 600 } 35 | # Order refresh process to apply taxes. 36 | # We set the priority very low so it calculates last. 37 | commerce_examples.order_process.taxes: 38 | class: Drupal\commerce_examples\OrderProcessor\ApplyTaxAdjustments 39 | tags: 40 | - { name: commerce_order.order_processor, priority: -300 } 41 | 42 | 43 | commerce_examples.generate_order: 44 | class: Drupal\commerce_examples\GenerateOrder 45 | arguments: ['@entity_type.manager', '@http_client', '@commerce_price.rounder'] 46 | -------------------------------------------------------------------------------- /config/install/user.role.store_admin.yml: -------------------------------------------------------------------------------- 1 | langcode: en 2 | status: true 3 | dependencies: { } 4 | id: store_admin 5 | label: 'Store Admin' 6 | weight: 3 7 | is_admin: null 8 | permissions: 9 | - 'access administration pages' 10 | - 'access commerce administration pages' 11 | - 'access commerce_order overview' 12 | - 'access commerce_product overview' 13 | - 'access commerce_product_attribute overview' 14 | - 'access commerce_promotion overview' 15 | - 'access commerce_shipping_method overview' 16 | - 'access commerce_store overview' 17 | - 'access profile overview' 18 | - 'access site reports' 19 | - 'access toolbar' 20 | - 'access tour' 21 | - 'access user profiles' 22 | - 'administer commerce_checkout_flow' 23 | - 'administer commerce_currency' 24 | - 'administer commerce_order' 25 | - 'administer commerce_order_type' 26 | - 'administer commerce_package_type' 27 | - 'administer commerce_payment' 28 | - 'administer commerce_payment_gateway' 29 | - 'administer commerce_payment_method' 30 | - 'administer commerce_product' 31 | - 'administer commerce_product_attribute' 32 | - 'administer commerce_product_type' 33 | - 'administer commerce_promotion' 34 | - 'administer commerce_shipment' 35 | - 'administer commerce_shipment_type' 36 | - 'administer commerce_shipping_method' 37 | - 'administer commerce_store' 38 | - 'administer commerce_store_type' 39 | - 'administer profile' 40 | - 'administer profile types' 41 | - 'create commerce_product_attribute' 42 | - 'create commerce_promotion' 43 | - 'create commerce_shipping_method' 44 | - 'delete commerce_promotion' 45 | - 'delete commerce_shipping_method' 46 | - 'manage own commerce_payment_method' 47 | - 'update commerce_product_attribute' 48 | - 'update commerce_promotion' 49 | - 'update commerce_shipping_method' 50 | - 'view any commerce_product' 51 | - 'view any commerce_store' 52 | - 'view any profile' 53 | - 'view commerce_order' 54 | - 'view commerce_product_attribute' 55 | - 'view commerce_promotion' 56 | - 'view commerce_shipping_method' 57 | - 'view own commerce_order' 58 | - 'view own commerce_product' 59 | - 'view own commerce_store' 60 | - 'view own profile' 61 | - 'view the administration theme' 62 | -------------------------------------------------------------------------------- /commerce_examples.install: -------------------------------------------------------------------------------- 1 | getStorage('commerce_store'); 9 | 10 | $currency_importer->import('USD'); 11 | $store_values = [ 12 | 'type' => 'online', 13 | 'uid' => 1, 14 | 'name' => 'Demo store', 15 | 'mail' => 'admin@example.com', 16 | 'address' => [ 17 | 'country_code' => 'US', 18 | 'postal_code' => '29616', 19 | 'locality' => 'Greenville', 20 | 'address_line1' => 'PO Box 26851', 21 | 'administrative_area' => 'SC', 22 | ], 23 | 'billing_countries' => [ 24 | 'US', 25 | ], 26 | 'shipping_countries' => [ 27 | 'US', 28 | ], 29 | 'default_currency' => 'USD', 30 | ]; 31 | /** @var \Drupal\commerce_store\Entity\StoreInterface $store */ 32 | $store = $store_storage->create($store_values); 33 | $store->save(); 34 | $store_storage->markAsDefault($store); 35 | 36 | // Enable shipping on the default order type. 37 | $default_order_type = OrderType::load('default'); 38 | $default_order_type->setThirdPartySetting('commerce_checkout', 'checkout_flow', 'shipping'); 39 | 40 | /** @var \Drupal\commerce\ConfigurableFieldManagerInterface $configurable_field_manager */ 41 | $configurable_field_manager = \Drupal::service('commerce.configurable_field_manager'); 42 | $field_definition = commerce_shipping_build_shipment_field_definition($default_order_type->id()); 43 | $configurable_field_manager->createField($field_definition); 44 | 45 | $default_order_type->setThirdPartySetting('commerce_shipping', 'shipment_type', 'default'); 46 | $default_order_type->save(); 47 | 48 | // Create demo shipping method. 49 | $shipping_method = ShippingMethod::create([ 50 | 'stores' => [$store->id()], 51 | 'name' => 'Table rate', 52 | 'status' => 1, 53 | 'plugin' => [ 54 | 'target_plugin_id' => 'demo_table_rate', 55 | 'target_plugin_configuration' => [], 56 | ], 57 | ]); 58 | $shipping_method->save(); 59 | 60 | \Drupal::getContainer()->get('commerce_examples.migration_runner')->run(); 61 | } 62 | -------------------------------------------------------------------------------- /src/Demo/MigrationRunner.php: -------------------------------------------------------------------------------- 1 | manager = $manager; 31 | } 32 | 33 | /** 34 | * Import content from migrations. 35 | */ 36 | public function run() { 37 | $this->execute('import'); 38 | } 39 | 40 | /** 41 | * Remove content from migrations. 42 | */ 43 | public function remove() { 44 | $this->execute('rollback'); 45 | } 46 | 47 | /** 48 | * Import or remove content from migrations. 49 | * 50 | * @param string $method_name 51 | * The method to execute: import or rollback. 52 | */ 53 | protected function execute($method_name) { 54 | $migration_ids = [ 55 | 'commerce_examples_product_attribute_color', 56 | 'commerce_examples_product_attribute_size', 57 | 'commerce_examples_product_variation_import_tshirt', 58 | 'commerce_examples_product_import_tshirt', 59 | 60 | 'commerce_examples_product_variation_import_ebook', 61 | 'commerce_examples_product_import_ebook', 62 | ]; 63 | 64 | array_walk($migration_ids, function ($migration_id) use ($method_name) { 65 | /** @var \Drupal\migrate\Plugin\Migration $migration */ 66 | if (!$migration = $this->manager->createInstance($migration_id)) { 67 | throw new PluginNotFoundException($migration_id); 68 | } 69 | $migrate_executable = (new MigrateExecutable($migration, new MigrateMessage())); 70 | $result = call_user_func_array([$migrate_executable, $method_name], []); 71 | if ($result != MigrationInterface::RESULT_COMPLETED) { 72 | throw new \Exception($migration->id() . ' failed'); 73 | } 74 | }); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/Plugin/migrate/process/Fillerama.php: -------------------------------------------------------------------------------- 1 | getModuleHandler(); 28 | $module = $module_handler->getModule('commerce_examples'); 29 | $filler = Json::decode(file_get_contents(DRUPAL_ROOT . '/' . $module->getPath() . '/data/fillerama.json')); 30 | 31 | $this->fillerQuotes = $filler['db']; 32 | $this->fillerHeadings = $filler['headers']; 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) { 39 | $data = []; 40 | 41 | $data[] = $this->getRandomHeader(); 42 | $data[] = $this->getRandomParagraph(); 43 | $data[] = ''; 44 | $data[] = $this->getRandomParagraph(mt_rand(5, 10)); 45 | 46 | return implode('', $data); 47 | } 48 | 49 | /** 50 | * Gets the module handler. 51 | * 52 | * @return \Drupal\Core\Extension\ModuleHandlerInterface 53 | * The module handler. 54 | */ 55 | protected function getModuleHandler() { 56 | if (!isset($this->moduleHandler)) { 57 | $this->moduleHandler = \Drupal::moduleHandler(); 58 | } 59 | return $this->moduleHandler; 60 | } 61 | 62 | /** 63 | * Get a random header text. 64 | */ 65 | protected function getRandomHeader($type = 'h2') { 66 | return "<$type>" . $this->fillerHeadings[array_rand($this->fillerHeadings)]['header'] . ""; 67 | } 68 | 69 | /** 70 | * Get a random paragraph text. 71 | */ 72 | protected function getRandomParagraph($max_lines = 4) { 73 | $sentences = []; 74 | 75 | for ($i = 0; $i < $max_lines; $i++) { 76 | $sentences[] = $this->fillerQuotes[array_rand($this->fillerQuotes)]['quote']; 77 | } 78 | 79 | return '

' . implode(' ', $sentences) . '

'; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/Plugin/Commerce/ShippingMethod/Table.php: -------------------------------------------------------------------------------- 1 | services['default'] = new ShippingService('default', 'Ground shipping'); 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function defaultConfiguration() { 43 | return [ 44 | 'services' => ['default'], 45 | ] + parent::defaultConfiguration(); 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function calculateRates(ShipmentInterface $shipment) { 52 | // Rate IDs aren't used in a flat rate scenario because there's always a 53 | // single rate per plugin, and there's no support for purchasing rates. 54 | $rate_id = 0; 55 | 56 | // Table rate starts at $5.00. 57 | $rate_amount = '5.00'; 58 | 59 | $order_total_number = $shipment->getOrder()->getTotalPrice()->getNumber(); 60 | 61 | if ($order_total_number > '100') { 62 | $rate_amount = '12.00'; 63 | } 64 | elseif ($order_total_number > '50') { 65 | $rate_amount = '9.00'; 66 | } 67 | elseif ($order_total_number > '25') { 68 | $rate_amount = '7.25'; 69 | } 70 | 71 | $amount = new Price($rate_amount, $shipment->getOrder()->getTotalPrice()->getCurrencyCode()); 72 | $rates = []; 73 | $rates[] = new ShippingRate($rate_id, $this->services['default'], $amount); 74 | 75 | return $rates; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /commerce_examples.info.yml: -------------------------------------------------------------------------------- 1 | name: Commerce Examples 2 | type: module 3 | description: Commerce example stuff 4 | core: 8.x 5 | package: Custom 6 | dependencies: 7 | - file 8 | - image 9 | - commerce_store 10 | - commerce_product 11 | - commerce_payment_example 12 | - migrate 13 | - commerce_shipping 14 | 15 | config_devel: 16 | install: 17 | - commerce_order.commerce_order_type.b2b 18 | - commerce_order.commerce_order_type.digital 19 | - commerce_payment.commerce_payment_gateway.example_on_site 20 | - commerce_product.commerce_product_attribute.color 21 | - commerce_product.commerce_product_attribute.size 22 | - commerce_product.commerce_product_type.ebook 23 | - commerce_product.commerce_product_type.t_shirt 24 | - commerce_product.commerce_product_variation_type.ebook 25 | - commerce_product.commerce_product_variation_type.t_shirt 26 | - core.entity_form_display.commerce_order.digital.default 27 | - core.entity_form_display.commerce_product.t_shirt.default 28 | - core.entity_form_display.commerce_product_variation.t_shirt.default 29 | - core.entity_view_display.commerce_product.ebook.default 30 | - core.entity_view_display.commerce_product.t_shirt.default 31 | - core.entity_view_display.commerce_product_attribute_value.color.default 32 | - core.entity_view_display.commerce_product_variation.ebook.default 33 | - core.entity_view_display.commerce_product_variation.t_shirt.default 34 | - field.field.commerce_order.digital.order_items 35 | - field.field.commerce_product.ebook.body 36 | - field.field.commerce_product.ebook.stores 37 | - field.field.commerce_product.ebook.variations 38 | - field.field.commerce_product.t_shirt.body 39 | - field.field.commerce_product.t_shirt.stores 40 | - field.field.commerce_product.t_shirt.variations 41 | - field.field.commerce_product_attribute_value.color.field_swatch 42 | - field.field.commerce_product_variation.t_shirt.attribute_color 43 | - field.field.commerce_product_variation.t_shirt.attribute_size 44 | - field.field.commerce_product_variation.t_shirt.weight 45 | - field.storage.commerce_product_attribute_value.field_swatch 46 | - field.storage.commerce_product_variation.attribute_color 47 | - field.storage.commerce_product_variation.attribute_size 48 | - field.storage.commerce_product_variation.weight 49 | - migrate_plus.migration.commerce_examples_product_attribute_color 50 | - migrate_plus.migration.commerce_examples_product_attribute_size 51 | - migrate_plus.migration.commerce_examples_product_import_ebook 52 | - migrate_plus.migration.commerce_examples_product_import_tshirt 53 | - migrate_plus.migration.commerce_examples_product_variation_import_ebook 54 | - migrate_plus.migration.commerce_examples_product_variation_import_tshirt 55 | - migrate_plus.migration_group.commerce_examples_ebook 56 | - migrate_plus.migration_group.commerce_examples_tshirt 57 | - field.field.commerce_order.default.shipments 58 | - field.storage.commerce_order.shipments 59 | - user.role.store_admin 60 | -------------------------------------------------------------------------------- /src/Demo/CSVFileObject.php: -------------------------------------------------------------------------------- 1 | setFlags(CSVFileObject::READ_CSV | CSVFileObject::READ_AHEAD | CSVFileObject::DROP_NEW_LINE | CSVFileObject::SKIP_EMPTY); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function rewind() { 46 | $this->seek($this->getHeaderRowCount()); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function current() { 53 | $row = parent::current(); 54 | 55 | if ($row && !empty($this->columnNames)) { 56 | // Only use columns specified in the defined CSV columns. 57 | $row = array_intersect_key($row, $this->columnNames); 58 | // Set meaningful keys for the columns mentioned in $this->csvColumns. 59 | foreach ($this->columnNames as $key => $value) { 60 | // Copy value to more descriptive key and unset original. 61 | $value = key($value); 62 | $row[$value] = isset($row[$key]) ? $row[$key] : NULL; 63 | unset($row[$key]); 64 | } 65 | } 66 | 67 | return $row; 68 | } 69 | 70 | /** 71 | * Return a count of all available source records. 72 | */ 73 | public function count() { 74 | return iterator_count($this); 75 | } 76 | 77 | /** 78 | * Number of header rows. 79 | * 80 | * @return int 81 | * Get the number of header rows, zero if no header row. 82 | */ 83 | public function getHeaderRowCount() { 84 | return $this->headerRowCount; 85 | } 86 | 87 | /** 88 | * Number of header rows. 89 | * 90 | * @param int $header_row_count 91 | * Set the number of header rows, zero if no header row. 92 | */ 93 | public function setHeaderRowCount($header_row_count) { 94 | $this->headerRowCount = $header_row_count; 95 | } 96 | 97 | /** 98 | * CSV column names. 99 | * 100 | * @return array 101 | * Get CSV column names. 102 | */ 103 | public function getColumnNames() { 104 | return $this->columnNames; 105 | } 106 | 107 | /** 108 | * CSV column names. 109 | * 110 | * @param array $column_names 111 | * Set CSV column names. 112 | */ 113 | public function setColumnNames(array $column_names) { 114 | $this->columnNames = $column_names; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /tests/src/Kernel/DemoInstalledTest.php: -------------------------------------------------------------------------------- 1 | installSchema('system', 'router'); 39 | $this->installEntitySchema('user'); 40 | $this->installEntitySchema('commerce_store'); 41 | $this->installEntitySchema('commerce_product_attribute'); 42 | $this->installEntitySchema('commerce_product_attribute_value'); 43 | $this->installEntitySchema('commerce_product_variation'); 44 | $this->installEntitySchema('commerce_product_variation_type'); 45 | $this->installEntitySchema('commerce_product'); 46 | $this->installEntitySchema('commerce_product_type'); 47 | $this->installEntitySchema('commerce_shipment'); 48 | $this->installEntitySchema('commerce_shipment_type'); 49 | $this->installEntitySchema('commerce_shipping_method'); 50 | $this->installConfig(static::$modules); 51 | } 52 | 53 | /** 54 | * Test product and variation type imported. 55 | */ 56 | public function testProductType() { 57 | $product_type = ProductType::load('t_shirt'); 58 | $this->assertNotNull($product_type); 59 | $product_variation_type = ProductVariationType::load('t_shirt'); 60 | $this->assertNotNull($product_variation_type); 61 | } 62 | 63 | /** 64 | * Tests around attributes. 65 | */ 66 | public function testAttributesImported() { 67 | $color = ProductAttribute::load('color'); 68 | $this->assertNotNull($color); 69 | $size = ProductAttribute::load('size'); 70 | $this->assertNotNull($size); 71 | } 72 | 73 | /** 74 | * Tests that the demo store is installed. 75 | */ 76 | public function testStoreInstalled() { 77 | module_load_install('commerce_examples'); 78 | commerce_examples_install(); 79 | 80 | /** @var \Drupal\commerce_store\StoreStorageInterface $store_storage */ 81 | $store_storage = $this->container->get('entity_type.manager')->getStorage('commerce_store'); 82 | 83 | $demo_store = $store_storage->loadDefault(); 84 | $this->assertEquals('Demo store', $demo_store->label()); 85 | $this->assertEquals('admin@example.com', $demo_store->getEmail()); 86 | $this->assertEquals('USD', $demo_store->getDefaultCurrencyCode()); 87 | 88 | $demo_address = $demo_store->getAddress(); 89 | $this->assertEquals('US', $demo_address->getCountryCode()); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /tests/src/Kernel/RunMigrationTest.php: -------------------------------------------------------------------------------- 1 | installSchema('system', 'router'); 37 | $this->installEntitySchema('user'); 38 | $this->installEntitySchema('commerce_store'); 39 | $this->installEntitySchema('commerce_product_attribute'); 40 | $this->installEntitySchema('commerce_product_attribute_value'); 41 | $this->installEntitySchema('commerce_product_variation'); 42 | $this->installEntitySchema('commerce_product_variation_type'); 43 | $this->installEntitySchema('commerce_product'); 44 | $this->installEntitySchema('commerce_product_type'); 45 | $this->installConfig(static::$modules); 46 | 47 | $this->container->get('commerce_examples.migration_runner')->run(); 48 | } 49 | 50 | /** 51 | * Tests around attributes. 52 | */ 53 | public function testAttributesImported() { 54 | $color = ProductAttribute::load('color'); 55 | /** @var \Drupal\commerce_product\Entity\ProductAttributeValueInterface[] $color_values */ 56 | $color_values = $color->getValues(); 57 | $this->assertNotEmpty($color_values); 58 | $this->assertEquals(4, count($color_values)); 59 | 60 | $size = ProductAttribute::load('size'); 61 | $size_values = $size->getValues(); 62 | $this->assertNotEmpty($size_values); 63 | $this->assertEquals(3, count($size_values)); 64 | } 65 | 66 | /** 67 | * Tests that the products got imported. 68 | */ 69 | public function testProductsImported() { 70 | $products = Product::loadMultiple(); 71 | $this->assertNotEmpty($products); 72 | $this->assertEquals(5, count($products)); 73 | 74 | /** @var \Drupal\commerce_product\Entity\ProductInterface $product */ 75 | foreach ($products as $product) { 76 | switch ($product->label()) { 77 | case 'Commerce Guys Hoodie': 78 | $this->assertEquals(12, count($product->getVariations())); 79 | $default_variation = $product->getDefaultVariation(); 80 | $this->assertEquals(10, $default_variation->get('weight')->number); 81 | $this->assertEquals(' oz', $default_variation->get('weight')->unit); 82 | break; 83 | 84 | case 'Drupal Commerce Cart Shirt': 85 | $this->assertEquals(12, count($product->getVariations())); 86 | $default_variation = $product->getDefaultVariation(); 87 | $this->assertEquals(7, $default_variation->get('weight')->number); 88 | $this->assertEquals(' oz', $default_variation->get('weight')->unit); 89 | break; 90 | } 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # @file 2 | # .travis.yml - Drupal for Travis CI Integration 3 | # 4 | # Template provided by https://github.com/LionsAd/drupal_ti. 5 | 6 | language: php 7 | 8 | sudo: false 9 | 10 | php: 11 | - 7 12 | - 5.6 13 | - 5.5 14 | 15 | branches: 16 | except: 17 | - "7.x-1.x" 18 | 19 | matrix: 20 | fast_finish: true 21 | 22 | env: 23 | global: 24 | - PHANTOMJS2_VERSION="2.0.0" 25 | # add composer's global bin directory to the path 26 | # see: https://github.com/drush-ops/drush#install---composer 27 | - PATH="$PATH:$HOME/.composer/vendor/bin" 28 | 29 | # Configuration variables. 30 | - DRUPAL_TI_MODULE_NAME="commerce_examples" 31 | - DRUPAL_TI_SIMPLETEST_GROUP="commerce_examples" 32 | 33 | # Define runners and environment vars to include before and after the 34 | # main runners / environment vars. 35 | #- DRUPAL_TI_SCRIPT_DIR_BEFORE="./drupal_ti/before" 36 | #- DRUPAL_TI_SCRIPT_DIR_AFTER="./drupal_ti/after" 37 | 38 | # The environment to use, supported are: drupal-7, drupal-8 39 | - DRUPAL_TI_ENVIRONMENT="drupal-8" 40 | - DRUPAL_TI_CORE_BRANCH="8.3.x" 41 | 42 | # Drupal specific variables. 43 | - DRUPAL_TI_DB="drupal_travis_db" 44 | - DRUPAL_TI_DB_URL="mysql://root:@127.0.0.1/drupal_travis_db" 45 | # Note: Do not add a trailing slash here. 46 | - DRUPAL_TI_WEBSERVER_URL="http://127.0.0.1" 47 | - DRUPAL_TI_WEBSERVER_PORT="8080" 48 | 49 | # Simpletest specific commandline arguments, the DRUPAL_TI_SIMPLETEST_GROUP is appended at the end. 50 | - DRUPAL_TI_SIMPLETEST_ARGS="--verbose --color --concurrency 4 --url $DRUPAL_TI_WEBSERVER_URL:$DRUPAL_TI_WEBSERVER_PORT --types Simpletest" 51 | 52 | # === Behat specific variables. 53 | # This is relative to $TRAVIS_BUILD_DIR 54 | - DRUPAL_TI_BEHAT_DIR="./tests/behat" 55 | # These arguments are passed to the bin/behat command. 56 | - DRUPAL_TI_BEHAT_ARGS="" 57 | # Specify the filename of the behat.yml with the $DRUPAL_TI_DRUPAL_DIR variables. 58 | - DRUPAL_TI_BEHAT_YML="behat.yml.dist" 59 | # This is used to setup Xvfb. 60 | - DRUPAL_TI_BEHAT_SCREENSIZE_COLOR="1280x1024x16" 61 | # The version of selenium that should be used. 62 | - DRUPAL_TI_BEHAT_SELENIUM_VERSION="2.44" 63 | # Set DRUPAL_TI_BEHAT_DRIVER to "selenium" to use "firefox" or "chrome" here. 64 | - DRUPAL_TI_BEHAT_DRIVER="phantomjs" 65 | - DRUPAL_TI_BEHAT_BROWSER="firefox" 66 | 67 | # PHPUnit specific commandline arguments. 68 | - DRUPAL_TI_PHPUNIT_ARGS="" 69 | # Specifying the phpunit-core src/ directory is useful when e.g. a vendor/ 70 | # directory is present in the module directory, which phpunit would then 71 | # try to find tests in. This option is relative to $TRAVIS_BUILD_DIR. 72 | #- DRUPAL_TI_PHPUNIT_CORE_SRC_DIRECTORY="./tests/src" 73 | 74 | # Code coverage via coveralls.io 75 | - DRUPAL_TI_COVERAGE="satooshi/php-coveralls:0.6.*" 76 | # This needs to match your .coveralls.yml file. 77 | - DRUPAL_TI_COVERAGE_FILE="build/logs/clover.xml" 78 | 79 | # Debug options 80 | #- DRUPAL_TI_DEBUG="-x -v" 81 | # Set to "all" to output all files, set to e.g. "xvfb selenium" or "selenium", 82 | # etc. to only output those channels. 83 | #- DRUPAL_TI_DEBUG_FILE_OUTPUT="selenium xvfb webserver" 84 | - DRUPAL_TI_MODULES_PATH="modules/contrib" 85 | 86 | matrix: 87 | # [[[ SELECT ANY OR MORE OPTIONS ]]] 88 | #- DRUPAL_TI_RUNNERS="phpunit" 89 | - DRUPAL_TI_RUNNERS="phpunit-core" 90 | #- DRUPAL_TI_RUNNERS="behat" 91 | #- DRUPAL_TI_RUNNERS="phpunit simpletest behat" 92 | 93 | mysql: 94 | database: drupal_travis_db 95 | username: root 96 | encoding: utf8 97 | 98 | before_install: 99 | # Remove xdebug. We aren't generating code coverage, and it slows down Composer. 100 | - phpenv config-rm xdebug.ini || true 101 | - composer global require "hirak/prestissimo:^0.3" 102 | - composer global require "lionsad/drupal_ti:dev-master#396d11d200005eb68491d24170da0a98ae7f51b3" 103 | - composer global require "squizlabs/php_codesniffer:2.*" 104 | - composer global require "drupal/coder:8.2.*" 105 | - phpcs --config-set installed_paths $HOME/.composer/vendor/drupal/coder/coder_sniffer 106 | - drupal-ti before_install 107 | 108 | install: 109 | - drupal-ti install 110 | 111 | before_script: 112 | - drupal-ti before_script 113 | 114 | script: 115 | - phpcs --standard=Drupal src 116 | - phpcs --standard=Drupal tests 117 | - drupal-ti script 118 | 119 | after_script: 120 | - drupal-ti after_script 121 | 122 | notifications: 123 | email: false 124 | -------------------------------------------------------------------------------- /src/Plugin/migrate/source/CSV.php: -------------------------------------------------------------------------------- 1 | configuration['path'])) { 60 | throw new MigrateException('You must declare the "path" to the source CSV file in your source settings.'); 61 | } 62 | 63 | // Key field(s) are required. 64 | if (empty($this->configuration['keys'])) { 65 | throw new MigrateException('You must declare "keys" as a unique array of fields in your source settings.'); 66 | } 67 | 68 | $this->fileClass = empty($configuration['file_class']) ? 'Drupal\commerce_examples\Demo\CSVFileObject' : $configuration['file_class']; 69 | } 70 | 71 | /** 72 | * Return a string representing the source file path. 73 | * 74 | * @return string 75 | * The file path. 76 | */ 77 | public function __toString() { 78 | return $this->configuration['path']; 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | */ 84 | public function initializeIterator() { 85 | // File handler using header-rows-respecting extension of SPLFileObject. 86 | $module_handler = $this->getModuleHandler(); 87 | $migration_info = $this->migration->getPluginDefinition(); 88 | $module = $module_handler->getModule($migration_info['provider']); 89 | $this->file = new $this->fileClass(DRUPAL_ROOT . '/' . $module->getPath() . '/' . $this->configuration['path']); 90 | 91 | // Set basics of CSV behavior based on configuration. 92 | $delimiter = !empty($this->configuration['delimiter']) ? $this->configuration['delimiter'] : ','; 93 | $enclosure = !empty($this->configuration['enclosure']) ? $this->configuration['enclosure'] : '"'; 94 | $escape = !empty($this->configuration['escape']) ? $this->configuration['escape'] : '\\'; 95 | $this->file->setCsvControl($delimiter, $enclosure, $escape); 96 | 97 | // Figure out what CSV column(s) to use. Use either the header row(s) or 98 | // explicitly provided column name(s). 99 | if (!empty($this->configuration['header_row_count'])) { 100 | $this->file->setHeaderRowCount($this->configuration['header_row_count']); 101 | 102 | // Find the last header line. 103 | $this->file->rewind(); 104 | $this->file->seek($this->file->getHeaderRowCount() - 1); 105 | 106 | $row = $this->file->current(); 107 | foreach ($row as $header) { 108 | $header = trim($header); 109 | $column_names[] = [$header => $header]; 110 | } 111 | $this->file->setColumnNames($column_names); 112 | } 113 | // An explicit list of column name(s) will override any header row(s). 114 | if (!empty($this->configuration['column_names'])) { 115 | $this->file->setColumnNames($this->configuration['column_names']); 116 | } 117 | 118 | return $this->file; 119 | } 120 | 121 | /** 122 | * {@inheritdoc} 123 | */ 124 | public function getIds() { 125 | $ids = []; 126 | foreach ($this->configuration['keys'] as $delta => $value) { 127 | if (is_array($value)) { 128 | $ids[$delta] = $value; 129 | } 130 | else { 131 | $ids[$value]['type'] = 'string'; 132 | } 133 | } 134 | return $ids; 135 | } 136 | 137 | /** 138 | * {@inheritdoc} 139 | */ 140 | public function fields() { 141 | $fields = []; 142 | foreach ($this->getIterator()->getColumnNames() as $column) { 143 | $fields[key($column)] = reset($column); 144 | } 145 | 146 | // Any caller-specified fields with the same names as extracted fields will 147 | // override them; any others will be added. 148 | if (!empty($this->configuration['fields'])) { 149 | $fields = $this->configuration['fields'] + $fields; 150 | } 151 | 152 | return $fields; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/GenerateOrder.php: -------------------------------------------------------------------------------- 1 | entityTypeManager = $entityTypeManager; 54 | $this->variantStorage = $entityTypeManager->getStorage('commerce_product_variation'); 55 | $this->orderItemStorage = $entityTypeManager->getStorage('commerce_order_item'); 56 | $this->orderStorage = $entityTypeManager->getStorage('commerce_order'); 57 | $this->client = $client; 58 | $this->rounder = $rounder; 59 | } 60 | 61 | public function bulkCreate() { 62 | $start = new \DateTime(); 63 | $start->modify('-1 year'); 64 | 65 | $end = new \DateTime(); 66 | $end->modify('today'); 67 | 68 | for ($i = $start; $i <= $end; $i->modify('+1 day')) { 69 | $how_many = rand(3, 15); 70 | for ($x = 0; $x < $how_many; $x++) { 71 | $this->create($i); 72 | print PHP_EOL . $i->format(\DateTime::ISO8601); 73 | } 74 | $event_dispatcher = \Drupal::getContainer()->get('event_dispatcher'); 75 | $event_dispatcher->dispatch( 76 | KernelEvents::TERMINATE, 77 | new PostResponseEvent(\Drupal::getContainer()->get('kernel'), new Request(), new Response()) 78 | ); 79 | } 80 | } 81 | 82 | public function create(\DateTime $when = NULL) { 83 | if (!$when) { 84 | $when = new \DateTime(); 85 | $when->modify('-1 month'); 86 | } 87 | 88 | $person = $this->getRandomUser(); 89 | $address = $this->getRandomAddress(); 90 | 91 | /** @var \Drupal\commerce_order\Entity\OrderInterface $order */ 92 | $order = $this->orderStorage->create([ 93 | 'uid' => 0, 94 | 'type' => 'default', 95 | ]); 96 | $order->setEmail($person['email']); 97 | $order->setPlacedTime($when->getTimestamp()); 98 | $order->setCreatedTime($when->getTimestamp()); 99 | $order->setStore(Store::load(1)); 100 | $order->setIpAddress($this->getRandomIp()); 101 | 102 | $variations = $this->getRandomVariants(); 103 | $order_items = []; 104 | foreach ($variations as $variation) { 105 | $order_item = $this->orderItemStorage->createFromPurchasableEntity($variation); 106 | // Order item total calc happens on save. 107 | $total_price = $order_item->getUnitPrice()->multiply($order_item->getQuantity()); 108 | $order_item->total_price = $this->rounder->round($total_price); 109 | 110 | $order_items[] = $order_item; 111 | } 112 | 113 | $order->setItems($order_items); 114 | $order->recalculateTotalPrice(); 115 | 116 | $profile = Profile::create([ 117 | 'type' => 'customer', 118 | 'address' => [ 119 | 'organization' => '', 120 | 'country_code' => 'US', 121 | 'postal_code' => $address['postal_code'], 122 | 'locality' => $address['locality'], 123 | 'address_line1' => $address['address_line1'], 124 | 'administrative_area' => $address['administrative_area'], 125 | 'given_name' => ucfirst($person['name']['first']), 126 | 'family_name' => ucfirst($person['name']['last']), 127 | ], 128 | 'uid' => 0, 129 | ]); 130 | $profile->save(); 131 | $order->setBillingProfile($profile); 132 | $order->save(); 133 | 134 | $workflow = $order->getState()->getWorkflow(); 135 | $order->getState()->applyTransition($workflow->getTransition('place')); 136 | $order->save(); 137 | } 138 | 139 | /** 140 | * Gets a set of 1-4 random variations. 141 | * 142 | * @return \Drupal\commerce_product\Entity\ProductVariationInterface[] 143 | * The variations. 144 | */ 145 | protected function getRandomVariants() { 146 | $variations = $this->variantStorage->loadByProperties(['type' => 't_shirt']); 147 | 148 | $keys = array_rand($variations, rand(2, 5)); 149 | $demo_variations = []; 150 | foreach ($keys as $key) { 151 | $demo_variations[] = $variations[$key]; 152 | } 153 | 154 | return $demo_variations; 155 | } 156 | 157 | protected function getRandomIp() { 158 | $ips = [ 159 | '75.86.161.54', 160 | '75.26.161.58', 161 | '15.26.161.58', 162 | ]; 163 | 164 | return $ips[array_rand($ips)]; 165 | } 166 | 167 | protected function getRandomUser() { 168 | $person = $this->client->get('https://randomuser.me/api/'); 169 | $person = Json::decode($person->getBody()->getContents()); 170 | return $person['results'][0]; 171 | } 172 | 173 | protected function getRandomAddress() { 174 | $addresses = [ 175 | [ 176 | 'address_line1' => '8502 Pilgrim St.', 177 | 'locality' => 'Mokena', 178 | 'administrative_area' => 'IL', 179 | 'postal_code' => '60448', 180 | ], 181 | [ 182 | 'address_line1' => '7691 East 6th St', 183 | 'locality' => 'Lewiston', 184 | 'administrative_area' => 'ME', 185 | 'postal_code' => '04240', 186 | ], 187 | [ 188 | 'address_line1' => '315 Addison Court ', 189 | 'locality' => 'New Windsor', 190 | 'administrative_area' => 'NY', 191 | 'postal_code' => '12553', 192 | ], 193 | [ 194 | 'address_line1' => '45 Bow Ridge Ave', 195 | 'locality' => 'West Chicago', 196 | 'administrative_area' => 'IL', 197 | 'postal_code' => '60185', 198 | ], 199 | [ 200 | 'address_line1' => '4 Washington Avenue', 201 | 'locality' => 'Commack', 202 | 'administrative_area' => 'NY', 203 | 'postal_code' => '11725', 204 | ], 205 | [ 206 | 'address_line1' => '31 Fairfield Dr', 207 | 'locality' => 'Bonita Springs', 208 | 'administrative_area' => 'FL', 209 | 'postal_code' => '34135', 210 | ], 211 | [ 212 | 'address_line1' => '8757 Homestead St', 213 | 'locality' => 'Port Chester', 214 | 'administrative_area' => 'NY', 215 | 'postal_code' => '10573', 216 | ], 217 | [ 218 | 'address_line1' => '8281 Fawn St.', 219 | 'locality' => 'Strongsville', 220 | 'administrative_area' => 'OH', 221 | 'postal_code' => '44136', 222 | ], 223 | [ 224 | 'address_line1' => '7364 Wild Horse Street', 225 | 'locality' => 'Wake Forest', 226 | 'administrative_area' => 'NC', 227 | 'postal_code' => '27587', 228 | ], 229 | [ 230 | 'address_line1' => '7267 Surrey Ave', 231 | 'locality' => 'Butler', 232 | 'administrative_area' => 'PA', 233 | 'postal_code' => '16001', 234 | ], 235 | ]; 236 | 237 | return $addresses[array_rand($addresses)]; 238 | } 239 | 240 | } 241 | -------------------------------------------------------------------------------- /data/fillerama.json: -------------------------------------------------------------------------------- 1 | {"db":[{"source":"Luke Skywalker","quote":"You don't believe in the Force, do you?"},{"source":"Leia","quote":"I don't know what you're talking about. I am a member of the Imperial Senate on a diplomatic mission to Alderaan--"},{"source":"Darth Vader","quote":"Obi-Wan is here. The Force is with him."},{"source":"Darth Vader","quote":"He is here."},{"source":"Han Solo","quote":"I'm trying not to, kid."},{"source":"Darth Vader","quote":"Don't underestimate the Force."},{"source":"Luke Skywalker","quote":"I care. So, what do you think of her, Han?"},{"source":"Luke Skywalker","quote":"I care. So, what do you think of her, Han?"},{"source":"Han Solo","quote":"Hokey religions and ancient weapons are no match for a good blaster at your side, kid."},{"source":"Darth Vader","quote":"Don't be too proud of this technological terror you've constructed. The ability to destroy a planet is insignificant next to the power of the Force."},{"source":"Luke Skywalker","quote":"You mean it controls your actions?"},{"source":"Ben Kenobi","quote":"Remember, a Jedi can feel the Force flowing through him."},{"source":"Leia","quote":"I don't know what you're talking about. I am a member of the Imperial Senate on a diplomatic mission to Alderaan--"},{"source":"Darth Vader","quote":"She must have hidden the plans in the escape pod. Send a detachment down to retrieve them, and see to it personally, Commander. There'll be no one to stop us this time!"},{"source":"Han Solo","quote":"Still, she's got a lot of spirit. I don't know, what do you think?"},{"source":"Luke Skywalker","quote":"Look, I can take you as far as Anchorhead. You can get a transport there to Mos Eisley or wherever you're going."},{"source":"Leia","quote":"The more you tighten your grip, Tarkin, the more star systems will slip through your fingers."},{"source":"Han Solo","quote":"Hey, Luke! May the Force be with you."},{"source":"Han Solo","quote":"Look, I ain't in this for your revolution, and I'm not in it for you, Princess. I expect to be well paid. I'm in it for the money."},{"source":"Ben Kenobi","quote":"I suggest you try it again, Luke. This time, let go your conscious self and act on instinct."},{"source":"Darth Vader","quote":"He is here."},{"source":"Darth Vader","quote":"He is here."},{"source":"Leia","quote":"What?!"},{"source":"Darth Vader","quote":"What!?"},{"source":"Darth Vader","quote":"Don't underestimate the Force."},{"source":"Han Solo","quote":"Ye-ha!"},{"source":"Leia","quote":"I don't know what you're talking about. I am a member of the Imperial Senate on a diplomatic mission to Alderaan--"},{"source":"Leia","quote":"I don't know what you're talking about. I am a member of the Imperial Senate on a diplomatic mission to Alderaan--"},{"source":"Darth Vader","quote":"What!?"},{"source":"Luke Skywalker","quote":"But with the blast shield down, I can't even see! How am I supposed to fight?"},{"source":"Luke Skywalker","quote":"Oh God, my uncle. How am I ever gonna explain this?"},{"source":"Luke Skywalker","quote":"But with the blast shield down, I can't even see! How am I supposed to fight?"},{"source":"Luke Skywalker","quote":"You don't believe in the Force, do you?"},{"source":"Ben Kenobi","quote":"Remember, a Jedi can feel the Force flowing through him."},{"source":"Ben Kenobi","quote":"Partially, but it also obeys your commands."},{"source":"Leia","quote":"The more you tighten your grip, Tarkin, the more star systems will slip through your fingers."},{"source":"Luke Skywalker","quote":"Red Five standing by."},{"source":"Luke Skywalker","quote":"But with the blast shield down, I can't even see! How am I supposed to fight?"},{"source":"Ben Kenobi","quote":"I need your help, Luke. She needs your help. I'm getting too old for this sort of thing."},{"source":"Darth Vader","quote":"A tremor in the Force. The last time I felt it was in the presence of my old master."},{"source":"Ben Kenobi","quote":"Remember, a Jedi can feel the Force flowing through him."},{"source":"Ben Kenobi","quote":"In my experience, there is no such thing as luck."},{"source":"Darth Vader","quote":"You are a part of the Rebel Alliance and a traitor! Take her away!"},{"source":"Han Solo","quote":"Ye-ha!"},{"source":"Han Solo","quote":"What good is a reward if you ain't around to use it? Besides, attacking that battle station ain't my idea of courage. It's more like…suicide."},{"source":"Darth Vader","quote":"The plans you refer to will soon be back in our hands."},{"source":"Darth Vader","quote":"A tremor in the Force. The last time I felt it was in the presence of my old master."},{"source":"Luke Skywalker","quote":"Oh God, my uncle. How am I ever gonna explain this?"},{"source":"Darth Vader","quote":"Don't act so surprised, Your Highness. You weren't on any mercy mission this time. Several transmissions were beamed to this ship by Rebel spies. I want to know what happened to the plans they sent you."},{"source":"Darth Vader","quote":"Don't be too proud of this technological terror you've constructed. The ability to destroy a planet is insignificant next to the power of the Force."},{"source":"Luke Skywalker","quote":"I can't get involved! I've got work to do! It's not that I like the Empire, I hate it, but there's nothing I can do about it right now. It's such a long way from here."},{"source":"Darth Vader","quote":"Obi-Wan is here. The Force is with him."},{"source":"Darth Vader","quote":"Don't be too proud of this technological terror you've constructed. The ability to destroy a planet is insignificant next to the power of the Force."},{"source":"Luke Skywalker","quote":"You mean it controls your actions?"},{"source":"Ben Kenobi","quote":"In my experience, there is no such thing as luck."},{"source":"Darth Vader","quote":"The Force is strong with this one. I have you now."},{"source":"Ben Kenobi","quote":"Partially, but it also obeys your commands."},{"source":"Luke Skywalker","quote":"I want to come with you to Alderaan. There's nothing for me here now. I want to learn the ways of the Force and be a Jedi, like my father before me."},{"source":"Darth Vader","quote":"You are a part of the Rebel Alliance and a traitor! Take her away!"},{"source":"Darth Vader","quote":"The Force is strong with this one. I have you now."},{"source":"Darth Vader","quote":"She must have hidden the plans in the escape pod. Send a detachment down to retrieve them, and see to it personally, Commander. There'll be no one to stop us this time!"},{"source":"Luke Skywalker","quote":"You don't believe in the Force, do you?"},{"source":"Ben Kenobi","quote":"Partially, but it also obeys your commands."},{"source":"Darth Vader","quote":"Don't be too proud of this technological terror you've constructed. The ability to destroy a planet is insignificant next to the power of the Force."},{"source":"Darth Vader","quote":"Don't be too proud of this technological terror you've constructed. The ability to destroy a planet is insignificant next to the power of the Force."},{"source":"Luke Skywalker","quote":"I want to come with you to Alderaan. There's nothing for me here now. I want to learn the ways of the Force and be a Jedi, like my father before me."},{"source":"Leia","quote":"No! Alderaan is peaceful. We have no weapons. You can't possibly…"},{"source":"Darth Vader","quote":"You are a part of the Rebel Alliance and a traitor! Take her away!"},{"source":"Leia","quote":"Dantooine. They're on Dantooine."},{"source":"Han Solo","quote":"Kid, I've flown from one side of this galaxy to the other. I've seen a lot of strange stuff, but I've never seen anything to make me believe there's one all-powerful Force controlling everything. There's no mystical energy field that controls my destiny. It's all a lot of simple tricks and nonsense."},{"source":"Luke Skywalker","quote":"All right. Well, take care of yourself, Han. I guess that's what you're best at, ain't it?"},{"source":"Darth Vader","quote":"You are a part of the Rebel Alliance and a traitor! Take her away!"},{"source":"Luke Skywalker","quote":"Alderaan? I'm not going to Alderaan. I've got to go home. It's late, I'm in for it as it is."},{"source":"Darth Vader","quote":"Don't be too proud of this technological terror you've constructed. The ability to destroy a planet is insignificant next to the power of the Force."},{"source":"Darth Vader","quote":"Don't underestimate the Force."},{"source":"Darth Vader","quote":"You are a part of the Rebel Alliance and a traitor! Take her away!"},{"source":"Luke Skywalker","quote":"Look, I can take you as far as Anchorhead. You can get a transport there to Mos Eisley or wherever you're going."},{"source":"Han Solo","quote":"You're all clear, kid. Let's blow this thing and go home!"},{"source":"Darth Vader","quote":"I find your lack of faith disturbing."},{"source":"Han Solo","quote":"Hokey religions and ancient weapons are no match for a good blaster at your side, kid."},{"source":"Luke Skywalker","quote":"I can't get involved! I've got work to do! It's not that I like the Empire, I hate it, but there's nothing I can do about it right now. It's such a long way from here."},{"source":"Leia","quote":"I'm surprised you had the courage to take the responsibility yourself."},{"source":"Leia","quote":"The more you tighten your grip, Tarkin, the more star systems will slip through your fingers."},{"source":"Darth Vader","quote":"Don't underestimate the Force."},{"source":"Ben Kenobi","quote":"I need your help, Luke. She needs your help. I'm getting too old for this sort of thing."},{"source":"Leia","quote":"What?!"},{"source":"Leia","quote":"I don't know what you're talking about. I am a member of the Imperial Senate on a diplomatic mission to Alderaan--"},{"source":"Han Solo","quote":"Still, she's got a lot of spirit. I don't know, what do you think?"},{"source":"Darth Vader","quote":"As you wish."},{"source":"Luke Skywalker","quote":"You mean it controls your actions?"},{"source":"Luke Skywalker","quote":"Oh God, my uncle. How am I ever gonna explain this?"},{"source":"Luke Skywalker","quote":"Alderaan? I'm not going to Alderaan. I've got to go home. It's late, I'm in for it as it is."},{"source":"Leia","quote":"The more you tighten your grip, Tarkin, the more star systems will slip through your fingers."},{"source":"Leia","quote":"I'm surprised you had the courage to take the responsibility yourself."},{"source":"Darth Vader","quote":"A tremor in the Force. The last time I felt it was in the presence of my old master."},{"source":"Darth Vader","quote":"Escape is not his plan. I must face him, alone."},{"source":"Luke Skywalker","quote":"I want to come with you to Alderaan. There's nothing for me here now. I want to learn the ways of the Force and be a Jedi, like my father before me."},{"source":"Darth Vader","quote":"He is here."},{"source":"Ben Kenobi","quote":"Your eyes can deceive you. Don't trust them."},{"source":"Luke Skywalker","quote":"I can't get involved! I've got work to do! It's not that I like the Empire, I hate it, but there's nothing I can do about it right now. It's such a long way from here."}],"headers":[{"header":"The Clone Wars"},{"header":"A New Hope"},{"header":"Revenge of the Sith"},{"header":"Imperial Star Destroyer"},{"header":"The Battle for Endor"},{"header":"The Force Unleashed"},{"header":"Return of the Jedi"},{"header":"Rebel Mission to Ord Mantell"},{"header":"The Phantom Menace"},{"header":"Knights of the Old Republic"},{"header":"The Sith Lords"},{"header":"Attack of the Clones"},{"header":"The Rebel Force"},{"header":"The Republic"},{"header":"The Empire Strikes Back"},{"header":"Revenge of the Sith"},{"header":"Jedi Academy"}]} 2 | --------------------------------------------------------------------------------