Java Geometric Sequence Programming

Hi everyone, I need ideas on how to initialize a new program in which a class GeometricSequence will extend Sequence. It has to implement public void generate ( ), which generates the sequence via
= 0generator ( = 0, 1, , numTerms1) and puts it into a double []. In java, is
programmed via Math.pow ( a, b ). So once the sequence is generated, public void generate ( ) should call public void setTermArray ( double [] dA ).

For example, when SequenceTest is run using 0 = 3, generator = 4 and numTerms = 9, the output of
the program will look like what is shown below.

a0 = ? 3.0
generator = ? 4.0
number of terms = ? 9
------+------------
i | a_i
------+------------
0 | 3.0000
1 | 12.0000
2 | 48.0000
3 | 192.0000
4 | 768.0000
5 | 3072.0000
6 | 12288.0000
7 | 49152.0000
8 | 196608.0000
------+------------
sum = 262143.0000

I need suggestions on how to implement and call methods. I included some of my work in a file.

A Complete Guide to Email Marketing and Anti-Spam Laws

If you’re an email marketer, there are laws, and they are serious. So, to help you stay out of jail we have put together this article to help you understand anti-spam laws and cover some of the big dos and don’ts of email marketing.

There are protocols internationally that help prevent spam when it comes to email marketing. Many of these can lead to massive fines, penalties, and more when broken. Plus, your company’s or personal reputation can go down the drain with lousy email practices.

We’ll be covering spam, different types of emailing (legal vs. illegal), the CAN-SPAM Act, and all the essentials of ensuring you’re not sending out spam. You’ll understand what you can do and not do when it comes to email marketing.

This article will be going over:

So, let’s start with a good question…

What Is Spam?

Spam image of envelope with exclamation mark.
What is this “spam” stuff?

You’re probably familiar with the word “spam.” But, what is it exactly, and how is it defined?

Spam is unwanted junk email sent out in bulk to an indiscriminate list of recipients. Usually, spam is sent for commercial reasons.

A funny thing about it is the name. It’s from a Monty Python sketch, where the name of the canned pork product (Spam) is unavoidable and repetitive. Otherwise, spam definitely isn’t a laughing matter for those who receive it.

To elaborate a bit more, spam is unsolicited bulk email. The email recipient has not granted verifiable permission for the email to be sent to their inbox.

The keyword is bulk and unsolicited. Bulk means that the message was sent out to many inboxes, and unsolicited means not asked for. A message is spam only if it’s BOTH bulk and unsolicited.

A technical definition of spam, according to Spamhaus.org is:

“An electronic message is “spam” if (A) the recipient’s identity and context are irrelevant because the message is equally applicable to many other potential recipients; AND (B) the recipient has not verifiably granted deliberate, explicit, and still-revocable permission for it to be sent.”

Spam mostly boils down to consent. It doesn’t matter what the message is (e.g. scam, porn, meds, etc.). It would be spam if it were sent unsolicited and bulk.

It goes without saying, spam emails are typically irrelevant and not asked for. They can fill up an inbox, become annoying, and become a gateway for hackers.

The good news is there are anti-spam software and filters that scan emails for red flags and can often be caught and end up in your junk mail. Your email inbox provider (e.g. Gmail) will send an email through the filters built into their systems.

Filters look for suspicious subject lines, shortened URLs, and other factors—basically, red flags.

Spam prevention is constantly evolving. Spammers are clever and figuring out ways to get past software and filters regularly, while email companies are evolving to find those loopholes and fix them.

It’s a game of cat and mouse that goes on and on…

Spam vs. Opt-In Email

Image of two envelopes.
One is good and one is bad.

There can be a lot of confusion regarding what’s considered spam. This is especially true if you have someone that opts in to your email list. If you bulk send out an email to the list, is it spam?

Opt-In email is different from spam in the sense that you have permission to email that recipient – even if it’s in bulk. If you have a list of people that have agreed by either clicking or entering their email address, you’ll fall within the bounds of opt-in.

The difference between opting-in and spam is the approval factor. You’ll be sending content that the recipient wants to get. It’s not unsolicited.

That being said, sometimes, a recipient will mark an email as spam – even if they opted-in. It could be they forgot they signed up for permission to get the emails, or possibly your content DID become spammy (e.g. emailing way too much).

With proof from an email provider that a recipient did opt-in, sending out emails to that recipient is not considered spam. Spammy content, however, can get you to lose a contact and an ‘unsubscribe’, so be sure to practice good email etiquette.

What are Anti-Spam Laws?

Image of a gavel and envelope.
There are laws out there to help prevent spam.

In a nutshell, anti-spam laws are – you guessed it – rules based around unsolicited emails that help protect people from receiving unwanted spam emails.

In the U.S., an act called CAN-SPAM in 2003 pre-empted a handful of these laws. That said, most email service providers require that every user agree to abide by anti-spam policies via their terms of service.

If you’re wondering why it’s essential to follow anti-spam policies when sending out emails, consider this – a violation of anti-spam laws can result in fines up to $43K. Yikes!

These anti-spam laws are different in each country. You can view a list of various anti-spam laws by country here. I’ll also touch on a few other countries later on in this article.

It’s important to familiarize yourself with your market before sending unsolicited emails.

So, let’s get into a biggie that’s prominent in the United States: CAN-SPAM.

What Is the CAN-SPAM Act?

Image of hand writing on paper.
CAN-SPAM Act has some details you need to know about.

CAN-SPAM (Controlling the Assault of Non-Solicited Pornography And Marketing) is an act passed way back in 2003. It is a United States law that sets national standards for sending commercial emails.

It essentially establishes rules for commercial email and messages. It gives recipients the right to have a business or establishment stop emailing them, and, for those who violate the law, it outlines the penalties.

This law requires the Federal Trade Commission (FTC) to enforce its provisions.

Senators Conrad Burns and Ron Wyden sponsored the CAN-SPAM bill. After being passed, it was commonly referred to as the “You-Can-Spam” Act because the bill failed to prohibit various types of spam. For example, it doesn’t require marketers to get permission before sending an email.

It was required the FTC report back to Congress within 24 months of the passage to determine its effectiveness. On December 20, 2005, the FTC reported that the volume of spam was leveling off, and no changes were recommended.

There were a few modifications that came along. For example, Congress added the definition of the term “person” and modified the name “sender.” They clarified that a sender might comply with the act by adding a PO box or private mailbox.

The penalties for noncompliance with the CAN-SPAM Act can affect your pocketbook.

For EVERY email that violates the Act, you can get up to a $46,517 fine from the FTC. Think about what that would amount to if you mailed out to a list of 15,000. (Hint: It’s a lot…)

Yes, you want to be compliant.

CAN-SPAM Compliance

Image of a compliance checklist
Does your compliance check all of the boxes?

Marketers have to comply with requirements when it comes to email marketing and as part of the CAN-SPAM Act. The three basic types of compliance defined in the Act are as follows:

1. Unsubscribe Compliance: Email recipients must have a way of opting out of your emails. Unsubscribing is done simply by adding an unsubscribe link or having the recipient email you back for email list removal. Then, the opt-out request MUST be honored within ten business days.

The unsubscribe button
An example of the unsubscribe button on our The WhiP newsletter. (But, why would anyone ever unsubscribe from The WhiP?)

2. Content Compliance: This includes having accurate “From” lines, relevant subject lines, a legitimate physical address of the publisher or advertiser, and a warning if the content is adult.

3. Sending Behavior Compliance: There are a few things this includes. They are:

– A message can’t be sent without an unsubscribe option

– A message can’t contain a false header

– A message should contain a minimum of one sentence

– A message cannot be null

– A message unsubscribe option should be below the message

These three basic types of compliance, when followed, will keep you safe from getting fined.

To make it simple, here are the DOs and DON’Ts when it comes to email marketing and following the CAN-SPAM Act.

DO

  • Include your valid physical postal address in all outbound emails.
  • Have a simple and easy opt-out method included with every email, and honor the unsubscribe within ten business days.
  • Include a clear understanding of who it’s from, who it’s to, and “reply to” language that’s accurate.

DON’T

  • Make it hard to unsubscribe. You can’t charge a fee, make it terribly tough (e.g. including tons of steps), or ask to provide additional information other than an email address.
  • Don’t sell or transfer email addresses to a different list.
  • Avoid using any deceptive lines in your emails that misrepresent the content.

Keep in mind that this article isn’t legal advice. We’re not lawyers, so be sure to check out the FTC website for additional information or consult an attorney if you have some questions that you feel are not answered in this article.

Cold Emails

Image of a snow covered piece of mail.
Cold emails are snow much better than spam.

One common misconception is that you can’t legally send cold emails. That being said, you can. Legally.

It’s important to follow the guidelines listed above when sending emails. As long as you do so, you should be safe from fines. It’s how many marketers stay in business, and cold emails are sent regularly.

What’s the difference between a cold email and spam?

Like someone opting into a list, it’s geared towards that specific person. Successful emails include intentional and personalized contact with the email recipient.

They also communicate valuable information, aim to form a trusting relationship, and – of course – comply with components of the CAN-SPAM Act and include an unsubscribe link (or option).

International Anti-Spam and & Data Protection Laws

International spam laws image
Yes, there are spam laws worldwide.

There are similar laws in other countries that are comparable to the U.S. CAN-SPAM Act. So, if you’re marketing out to other countries, and you’re based in the U.S., you’ll need to comply with international law – or you could be held liable and get fined or face punishment (even imprisonment).

You may be familiar with GDPR (General Data Protection Regulation), which is Europe’s version of the CAN-SPAM Act. This law is implemented for all of the European Union states.

At a glance, the UK GDPR has seven principles:

  1. Lawfulness, fairness, and transparency
  2. Purpose limitation
  3. Data Minimisation
  4. Accuracy
  5. Storage limitation
  6. Integrity and confidentiality
  7. Accountability

For more on GDPR, be sure to read our comprehensive article that covers absolutely everything.

In Canada, there is the Canada Anti-Spam Legislation (CASL). It sets requirements for all commercial email messages and is similar to other regulations requiring businesses to identify themselves and provide an opt-out option.

Canada’s anti-spam legislation is considered one of the world’s most robust data protection regulations. The big difference is that CASL requires people to opt-in to get messages from brands. That means that brands can only market out to emails that gave consent.

The CASL recognizes two types of consent:

  1. Express Consent: This means that a person gives explicit verbal or written consent for emails. This type of consent doesn’t have an expiration date and remains valid until the user withdraws consent.
  2. Implied Consent: Some activities, like purchasing a service or inquiring about a product, can imply consent to receive emails. With Implied Consent, it does expire. It is valid for two years for purchase, and for an inquiry, it’s valid for six months. The recipient can renew consent by purchasing another product or another service inquiry.

Companies must keep a record of acquired permissions from subscribers. Violations of CASL can lead to the sender being sued by the recipient.

All CASL requirements are:

  1. The company must provide identifying information (e.g. business name, postal address, etc.).
  2. A sender can only email people who have given either express or implied consent.
  3. Records of consent must be kept.
  4. Contacts must be removed from the mailing list upon expiration of the consent.
  5. Corporate email messages must include an opt-out option, and the unsubscribe request must be honored within ten business days.

For more on CASL and its requirements, please visit their website.

Laws vary by country, so be sure to investigate exactly what your country requires in terms of anti-spam laws.

Email Marketing Services

To make sure you’re a law-abiding marketer, there are some great email companies out there.

These companies have built-in compliance with email regulations and policy requirements. Plus, they have feedback mechanisms (e.g. compliant warnings), so this helps to minimize the risk of incurring violations.

Please note that these are not affiliate companies with us. (We do not have affiliates and never will!). However, they’re all recommended by us and have a solid reputation.

These are just a handful of companies that are available. They’re all a safe bet that your email marketing is in good hands!

Along with a good email company, there are other steps you can take to…

Image of an envelope with a green checkmark
Ensure those emails you send check out.

We’ve covered a lot about email marketing and spam. So, to wrap things up, let’s take a look at how to ensure you’re completely legal when it comes to sending content to another inbox.

After all, it’s not just legalities – it’s your company’s or individual reputation. It’s obviously always good to follow the rules and comply with international anti-spam requirements.

1. Know Who You’re Emailing

Email allows you to reach out to practically anyone around the world. If you plan on doing so, be sure to get familiar with country-specific legalities (e.g. CAN-SPAM Act).

Of course, it can be tricky knowing where everyone is from in a contact list. You could always have an opt-in option for specific regions and segment accordingly. In general, most anti-spam laws have many of the same conditions, so keep that in mind.

2. Ensure Your Contact Has Opted-In

It’s not required in the United States to have your subscribers be opt-in subscribers, but other anti-spam laws do need it.

Opting-in will help protect you internationally and ensure that your subscribers are legit people wanting information.

3. Store Their Consent

Storing a contact’s consent in a safe and reliable place can help protect you and can be used to make your case legal. Have the ability to demonstrate what you told them they consented to and how they consented (e.g. by opting-in).

4. Have Opt-Out Option

We’ve talked about this earlier, but again, it’s vital to give your users the ability to opt-out and ensure they are opted out after a set amount of time.

5. Do Not Buy Email Lists

It is legal to buy email lists, but you’ll have to obtain specific consent from people on the list to keep using them. You can run into trouble if someone on that list opted out, and then you reach out to them since they’re now on your list, and unbeknownst to you, they didn’t want to be.

It’s best to collect leads through your website or opt-in form.

6. Be Open About Who You Are

Bottom line: Don’t hide your identity. Include your name, last name, and company where you work. Add links to the company website to ensure the contact knows who sent the email. Also, provide your mailing address.

Transparency is key to being extremely open about who you are as the sender.

7. Be Honest

Whatever your proposal is with your email, don’t mislead and be honest about it. Be sure it’s clear and straightforward. This goes for the subject line and content.

8. Send Quality (and not too many) Emails

There’s no legality about sending massive amounts emails to legit contacts, but for best practices and to help ensure that you don’t get dinged for anything illegal, practice good email policies and don’t oversend.

In terms of quality, avoid spammy things, like using all capital letters, too many exclamation points, and gimmicky words or phrases.

In other words, don’t make your quality, legit (and legal) emails look like spam by emailing too much or by containing spammy content.

9. Use a Compliant Email Service

Using a compliant email service (e.g. Mailchimp, Constant Contact, etc.) has numerous benefits to ensure you’re legal. They monitor bounce, unsubscribe & abuse rates, and issue warnings to accounts when exceeding industry standards.

Plus, they store information and keep a “paper trail” of opt-ins and help manage your audiences. They also have support, so any questions you may have about legalities, they can help answer.

Spam I Am (Not)

As you read, there’s a ton you can do to ensure you’re legally up to par when it comes to email marketing. It’s just a matter of putting good practices in place and maintaining quality standards that can be used internationally.

Also, just being familiar with the laws, what’s spam, and what isn’t will keep you from becoming a spammy marketer that’s not doing yourself – or your email list – any favors.

Luckily, you don’t have to worry about massive fines and penalties if you just play by the rules. If you even THINK you might be sending out Spam – you probably are. You now know better not to.

After all, there’s no glamor in being a spammer.

BP Rewrites Feature Plugin Now in Beta

The ball is moving on a nine-year-old effort to migrate BuddyPress’ custom URI parser to use WordPress’ Rewrite API.

BuddyPress currently requires sites to use pretty permalinks in order to be compatible with its URL parser. The plugin analyzes the URL for whatever component you are viewing and performs internal checks to decide what to display there and determine the proper template file. This custom parser has been working well for years but has some drawbacks compared to WordPress’ newer Rewrite API.

BuddyPress lead developer Boone Gorges summarized the problem in the original ticket:

BP’s custom URI parser (living mostly in bp_core_set_uri_globals()) is slow, error-prone, non-extensible, non-testable, and out of step with WP best practices.

In August of 2021, BuddyPress contributors moved this effort into the BP Rewrites feature plugin with the eventual goal of getting it merged into BP Core after wider testing. The first beta was released this week with a call for testing before it gets moved to the WordPress plugin directory.

BP Rewrites isn’t just an under-the-hood architectural improvement for BuddyPress, it also has several user-facing benefits:

  • BuddyPress URLs customization improved and easier
  • Compatibility with plain permalinks
  • Improved Compliance with WordPress Standards
  • Improved User/Advanded user/Plugin developer/Theme designer BuddyPress experience

Users who test the plugin will find a URLs customization screen (replacing the BP Pages screen) under the BuddyPress settings where page slugs can be easily changed. This is much more user-friendly than editing the wp-config.php file.

“When you deactivate the plugin, buddypress post type’s items are switched back to regular pages and you get them back into the corresponding WordPress Administation edit screen,” BuddyPress core developer Mathieu Viet said. “Post metas are still there in case you want to activate BP Rewrites back (this can happen when you’re testing another BuddyPress plugin). If you absolutely want to get rid of these post metas, you can delete the BuddyPress pages, create new ones and redo the page mapping from the BuddyPress Pages settings screen.”

This isn’t something you want to test on a production site, as it’s still uncertain how BP Rewrites will interact with different plugins. Testers can report bugs as issues on the plugin’s GitHub repository.

“There’s no secret: the only way to have enough confidence into this backward compatibility mechanism to start thinking of merging BP Rewrites into BuddyPress Core is to test, test, and test again,” Viet said.

When discussing BP Rewrites’ progress earlier this year, Viet advised the feature plugin stay as an add-on for at least two major BuddyPress releases. This will give developers the chance to ensure there are fewer plugin conflicts before merging into BuddyPress core.

Android Native – How to use TypeConverter for Room

Introduction

When working with a Room database, we are mostly restricted to save data using primitives (and boxed primitives). Reference types are not supported right out of the box, but can be enabled by creating additional TypeConverter. If you are familiar with ORM-light frameworks such as Spring JDBC, then you can think of TypeConverters as being conceptually similar to RowMapper.

In this tutorial, we will learn how to use TypeConverters in a Room database. Existing basic knowledge of Room is required for this tutorial.

Goals

At the end of the tutorial, you would have learned:

  1. How to create TypeConverters to save reference types in a Room database.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic Room.
  3. Basic Serialization.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the dependencies below into your Module build.gradle file inside the dependencies {} block.

     def roomVersion = "2.4.1"
    
     implementation "androidx.room:room-runtime:$roomVersion"
     annotationProcessor "androidx.room:room-compiler:$roomVersion"
    
     //To use Kotlin annotation processing tool (kapt)
     kapt "androidx.room:room-compiler:$roomVersion"
    
     //Kotlin Extensions and Coroutines support for Room
     implementation "androidx.room:room-ktx:$roomVersion"
    
     //lifecycle scope
     implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
  3. In the same file, add the kapt annotation processor inside the plugins {} block.

     id 'org.jetbrains.kotlin.kapt'
  4. Create a new file called Teacher.kt and add the code below.

     data class Teacher(
        val name: String,
        val age: Int
     ) {
        override fun toString(): String {
            return "$name:$age"
        }
     }
  5. Create a new file called Classroom.kt and add the code below.

     import androidx.room.ColumnInfo
     import androidx.room.Entity
     import androidx.room.PrimaryKey
    
     @Entity
     data class Classroom(
        @PrimaryKey(autoGenerate = true) val uid: Int = 0,
        val grade: Grade,
        @ColumnInfo(name = "homeroom_teacher") val homeroomTeacher: Teacher
     )
  6. Create a new file called ClassroomDao.kt and add the code below.

     import androidx.room.*
    
     @Dao
     interface ClassroomDao {
        @Query("SELECT * FROM classroom")
        fun getAll(): List<Classroom>
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        fun insertAll(vararg classrooms: Classroom)
     }
  7. Create a new file called Grade.kt and add the code below.

     enum class Grade {
        JUNIOR, SOPHOMORE, SENIOR
     }
  8. Create a new file called SchoolDatabase.kt and add the code below.

     import androidx.room.BuiltInTypeConverters
     import androidx.room.Database
     import androidx.room.RoomDatabase
     import androidx.room.TypeConverters
    
     @Database(entities = [Classroom::class], version = 1)
     //@TypeConverters(
     //    Converters::class,
     //    builtInTypeConverters = BuiltInTypeConverters(
     //        enums = BuiltInTypeConverters.State.DISABLED
     //    )
     //)
     abstract class SchoolDatabase : RoomDatabase() {
        abstract fun classroomDao(): ClassroomDao
     }
  9. Create a new file called Converters.kt and add the code below.

     package com.codelab.daniwebandroidroomdatabaseconverter
    
     import androidx.room.TypeConverter
    
     class Converters {
    
        @TypeConverter
        fun teacherToString(teacher: Teacher) = "$teacher" //Other options are json string, serialized blob
    
        @TypeConverter
        fun stringToTeacher(value: String): Teacher {
            val name = value.substringBefore(':')
            val age = value.substringAfter(':').toInt()
    
            return Teacher(name, age)
        }
    
     }
Project Overview

Our skeleton project for this tutorial is quite simple. We completely ignore the UI, ViewModel or Hilt DI to focus on the database here. The Room database SchoolDatabase includes a single table that stores classroom information. Each classroom contains an id, the Grade, and a Teacher.

Because Teacher is a reference type, so Room will refuse to compile for now. Upon compiling, we will receive the error messages below.

error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
    private final com.codelab.daniwebandroidroomdatabaseconverter.Teacher homeroomTeacher = null;

error: Cannot figure out how to read this field from a cursor.
    private final com.codelab.daniwebandroidroomdatabaseconverter.Teacher homeroomTeacher = null;

These errors occur because we have not registered any TypeConverter to our SchoolDatabase.

Creating Converters

To help Room understand how to convert these reference types, we will have to create TypeConverters. TypeConverters can only be used to convert a column. In the the picture below, a TypeConverter pair can be used to convert a column into a primitive that Room allows, and vice versa.

1.jpg

TypeConverters are just functions annotated with @TypeConverter. We have already created some TypeConverters in the file Converters.kt, so let us inspect some of them.

The first function that we are going to look at is teacherToString().

@TypeConverter
fun teacherToString(teacher: Teacher) = "$teacher" //Other options are json string, serialized blob

In teacherToString(), the only three things that really matter for Room are the parameter type, the return type, and the @TypeConverter annotation. The compiler uses information from them to determine whether Room can properly use them to convert from one type to another. The parameter type and the return type are reversed when it comes to stringToTeacher().

@TypeConverter
fun stringToTeacher(value: String): Teacher {
   val name = value.substringBefore(':')
   val age = value.substringAfter(':').toInt()

   return Teacher(name, age)
}

2.jpg

I have decided to store a string representation of a Teacher object in this tutorial because it is quick and simple. I have also overridden Teachers toString() to make the deserialization easier.

override fun toString(): String {
   return "$name:$age"
}

In real code, you can store your object in other ways, such as a serialized BLOB or a JSON string, with proper sanitization.

Pre-made TypeConverters

You might have noticed that I have not discussed the Grade enum at all. It is, after all, also a reference type. The simple reason why we do not have to provide TypeConverters for Grade is because Android already includes some premade TypeConverters from BuiltInTypeConverters.

Enums and UUID types are supported by default. The default support for enum uses the name() value. If that is not good enough for you, then you can also provide custom TypeConverters for Enum and UUID. Your custom TypeConverters take precedence over the builtin ones.

Register the Converters with Room

The next step that we would need to do is to register the Converters with the database. You can do that by applying an @Converters annotation to the RoomDatabase class. Note that @Converter and @Converters are different annotations. We already have the @TypeConverters annotation set up in SchoolDatabase.kt, but commented out. Uncomment it, and we will have the code below.

@TypeConverters(
   Converters::class,
   builtInTypeConverters = BuiltInTypeConverters(
       enums = BuiltInTypeConverters.State.DISABLED
   )
)

The builtInTypeConverters argument is entirely optional. If you do not provide it any value, then it will enable the default TypeConverters. If we run the App now, we will receive a compile error of:

error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
    private final com.codelab.daniwebandroidroomdatabaseconverter.Grade grade = null;

This is because we have told Room to disable the builtin TypeConverter for enums. We also did not provide any custom enum TypeConverter. Simply comment out the builtInTypeConverters argument for the code to compile.

@TypeConverters(
   Converters::class
/*    builtInTypeConverters = BuiltInTypeConverters(
       enums = BuiltInTypeConverters.State.DISABLED
   )*/
)

The Class object that we provided indicates that this class contains the @TypeConverter functions, so Room should look there for any type that it does not know how to convert.

Run the App

The app should compile correctly now, but it does not do anything useful yet.

  1. Add the top level variable below into MainActivity.kt.

     private const val TAG = "MAIN_ACTIVITY"
  2. Append the code below to MainActivity#onCreate().

     val db = Room.databaseBuilder(
        applicationContext,
        SchoolDatabase::class.java, "school-db"
     ).build()
    
     lifecycleScope.launch(Dispatchers.IO) {
        val classroomDao = db.classroomDao()
    
        val teacher1 = Teacher(
            name = "Mary",
            age = 35
        )
    
        val teacher2 = Teacher(
            name = "John",
            age = 28
        )
    
        val teacher3 = Teacher(
            name = "Diana",
            age = 46
        )
    
        val classroom1 = Classroom(
            grade = Grade.JUNIOR,
            homeroomTeacher = teacher1
        )
    
        val classroom2 = Classroom(
            grade = Grade.SOPHOMORE,
            homeroomTeacher = teacher2
        )
    
        val classroom3 = Classroom(
            grade = Grade.SENIOR,
            homeroomTeacher = teacher3
        )
    
        classroomDao.insertAll(
            classroom1,
            classroom2,
            classroom3
        )
    
        val classrooms = classroomDao.getAll()
    
        classrooms.forEach {
            Log.d(TAG, "$it")
        }
    
        db.clearAllTables()
     }

Run the app now. We should be able to see the output below in Logcat.

2022-02-14 13:59:18.700 12504-12551/com.codelab.daniwebandroidroomdatabaseconverter D/MAIN_ACTIVITY: Classroom(uid=1, grade=JUNIOR, homeroomTeacher=Mary:35)
2022-02-14 13:59:18.700 12504-12551/com.codelab.daniwebandroidroomdatabaseconverter D/MAIN_ACTIVITY: Classroom(uid=2, grade=SOPHOMORE, homeroomTeacher=John:28)
2022-02-14 13:59:18.701 12504-12551/com.codelab.daniwebandroidroomdatabaseconverter D/MAIN_ACTIVITY: Classroom(uid=3, grade=SENIOR, homeroomTeacher=Diana:46)
Solution Code

MainActivity.kt

package com.codelab.daniwebandroidroomdatabaseconverter

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.lifecycleScope
import androidx.room.Room
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

private const val TAG = "MAIN_ACTIVITY"

class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       val db = Room.databaseBuilder(
           applicationContext,
           SchoolDatabase::class.java, "school-db"
       ).build()

       lifecycleScope.launch(Dispatchers.IO) {
           val classroomDao = db.classroomDao()

           val teacher1 = Teacher(
               name = "Mary",
               age = 35
           )

           val teacher2 = Teacher(
               name = "John",
               age = 28
           )

           val teacher3 = Teacher(
               name = "Diana",
               age = 46
           )

           val classroom1 = Classroom(
               grade = Grade.JUNIOR,
               homeroomTeacher = teacher1
           )

           val classroom2 = Classroom(
               grade = Grade.SOPHOMORE,
               homeroomTeacher = teacher2
           )

           val classroom3 = Classroom(
               grade = Grade.SENIOR,
               homeroomTeacher = teacher3
           )

           classroomDao.insertAll(
               classroom1,
               classroom2,
               classroom3
           )

           val classrooms = classroomDao.getAll()

           classrooms.forEach {
               Log.d(TAG, "$it")
           }

           db.clearAllTables()
       }

   }
}

Classroom.kt

package com.codelab.daniwebandroidroomdatabaseconverter

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
data class Classroom(
   @PrimaryKey(autoGenerate = true) val uid: Int = 0,
   val grade: Grade,
   @ColumnInfo(name = "homeroom_teacher") val homeroomTeacher: Teacher
)

ClassroomDao

package com.codelab.daniwebandroidroomdatabaseconverter

import androidx.room.*

@Dao
interface ClassroomDao {
   @Query("SELECT * FROM classroom")
   fun getAll(): List<Classroom>

   @Insert(onConflict = OnConflictStrategy.REPLACE)
   fun insertAll(vararg classrooms: Classroom)
}

Converters.kt

package com.codelab.daniwebandroidroomdatabaseconverter

import androidx.room.TypeConverter

class Converters {

   @TypeConverter
   fun teacherToString(teacher: Teacher) = "$teacher" //Other options are json string, serialized blob

   @TypeConverter
   fun stringToTeacher(value: String): Teacher {
       val name = value.substringBefore(':')
       val age = value.substringAfter(':').toInt()

       return Teacher(name, age)
   }

}

Grade.kt

package com.codelab.daniwebandroidroomdatabaseconverter

enum class Grade {
   JUNIOR, SOPHOMORE, SENIOR
}

**SchoolDatabase.kt**

package com.codelab.daniwebandroidroomdatabaseconverter

import androidx.room.BuiltInTypeConverters
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters

@Database(entities = [Classroom::class], version = 1)
@TypeConverters(
   Converters::class
/*    builtInTypeConverters = BuiltInTypeConverters(
       enums = BuiltInTypeConverters.State.DISABLED
   )*/
)
abstract class SchoolDatabase : RoomDatabase() {
   abstract fun classroomDao(): ClassroomDao
}

Teacher.kt

package com.codelab.daniwebandroidroomdatabaseconverter

data class Teacher(
   val name: String,
   val age: Int
) {
   override fun toString(): String {
       return "$name:$age"
   }
}

Module **build.gradle**

plugins {
   id 'com.android.application'
   id 'org.jetbrains.kotlin.android'
   id 'org.jetbrains.kotlin.kapt'
}

android {
   compileSdk 32

   defaultConfig {
       applicationId "com.codelab.daniwebandroidroomdatabaseconverter"
       minSdk 21
       targetSdk 32
       versionCode 1
       versionName "1.0"

       testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }

   buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
   }
   compileOptions {
       sourceCompatibility JavaVersion.VERSION_1_8
       targetCompatibility JavaVersion.VERSION_1_8
   }
   kotlinOptions {
       jvmTarget = '1.8'
   }
}

dependencies {
   def roomVersion = "2.4.1"

   implementation "androidx.room:room-runtime:$roomVersion"
   annotationProcessor "androidx.room:room-compiler:$roomVersion"

   //To use Kotlin annotation processing tool (kapt)
   kapt "androidx.room:room-compiler:$roomVersion"

   //Kotlin Extensions and Coroutines support for Room
   implementation "androidx.room:room-ktx:$roomVersion"

   //lifecycle scope
   implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'

   implementation 'androidx.core:core-ktx:1.7.0'
   implementation 'androidx.appcompat:appcompat:1.4.1'
   implementation 'com.google.android.material:material:1.5.0'
   implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
   testImplementation 'junit:junit:4.13.2'
   androidTestImplementation 'androidx.test.ext:junit:1.1.3'
   androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
Summary

We have learned how to use TypeConverters in a Room database. The full project code can be found at https://github.com/dmitrilc/DaniwebAndroidRoomDatabaseConverter.

Android Native – RecyclerView swipe-to-remove and drag-to-reorder

Introduction

In this tutorial, we will learn how to add swipe-to-remove and drag-to-reorder functionalities into RecyclerView.

Goals

At the end of the tutorial, you would have learned:

  1. How to add swipe-to-remove and drag-to-reorder functionality to a RecyclerView.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic RecyclerView.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Replace the content of activity_main.xml with the code below.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  3. Create a new layout resource called item_view.xml. This is the ViewHolders layout. Copy and paste the code below.

     <?xml version="1.0" encoding="utf-8"?>
     <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/itemView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:gravity="center"
        android:textSize="32sp"
        tools:text="TextView" />
  4. Create a new Kotlin file called Item.kt. Copy and paste the code below.

     data class Item(
        val content: String
     )
  5. Create a new Kotlin file called ItemAdapter.kt. Copy and paste the code below.

     import android.view.LayoutInflater
     import android.view.View
     import android.view.ViewGroup
     import android.widget.TextView
     import androidx.recyclerview.widget.RecyclerView
    
     class ItemAdapter(private val dataset: List<Item>): RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {
    
        class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
            val textView: TextView = itemView.findViewById(R.id.itemView)
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.item_view, parent, false)
    
            return ItemViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
            holder.textView.text = dataset[position].content
        }
    
        override fun getItemCount() = dataset.size
     }
  6. In MainActivity#onCreate(), append the code below.

     //Creates a list of 10 elements
     val dataset = MutableList(10){
        Item("I am Item #$it")
     }
    
     //finds the RecyclerView
     val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
    
     //Assigns an adapter to RecyclerView
     recyclerView.adapter = ItemAdapter(dataset)
Project Overview

Currently, our project will compile and run just fine. All we have at the moment is a vanilla recycler with 10 items. As you can see from the Gif below with the pointer tracking option on, our RecyclerView currently does not register swiping and hold-to-drag actions.

VanillaRecycler.gif

At the end of the tutorial, we should be able to swipe to remove and hold-to-drag-to-reorder the RecyclerView.

ItemTouchHelper

ItemTouchHelper is a class provided by Android specifically to implement the functionalities that we want. Its most basic properties are:

  1. ItemTouchHelper.Callback: this is an abstract inner class. It is meant to be used only if you require fine control over the swipe and drop behaviors.
  2. ItemTouchHelper.SimpleCallback: this is also an abstract inner class. It already implements ItemTouchHelper.Callback, so we only have to implement two simple methods, onMove() and onSwipe(), to decide what to do when swiping and dragging actions are recorded.
  3. UP, DOWN, LEFT, RIGHT, START, END constants: These constants represent the actions that the user took, which the ItemTouchHelper.Callback class can use.

The picture below provides a rough picture of the relationship among the classes. LEFT and RIGHT are not listed because START and END can perform the same thing for both LTR and RTL layouts.

ItemTouchHelper.jpg

Implementing ItemTouchHelper.SimpleCallback

There are about 3 things that we need to perform to implement swipe-to-remove and drag-to-move.

  1. Create an ItemTouchHelper.SimpleCallback.
  2. Use that callback to create an ItemTouchHelper.
  3. Attaches the ItemTouchHelper to the RecyclerView.

Follow the steps below to implement ItemTouchHelper.SimpleCallback.

  1. Append the code below into MainActivity#onCreate(). Notice that we had to provide the bitwise or result of the direction constants.

     //Implementing the callback.
     val callback = object : ItemTouchHelper.SimpleCallback(
        ItemTouchHelper.UP or ItemTouchHelper.DOWN, //bitwise OR
        ItemTouchHelper.START or ItemTouchHelper.END //bitwise OR
     ) {
    
     }
  2. We have not implemented the required members yet, so let us start with the first required, onMove(). This function determines what to do during drag-and-drop behaviors. All we had to do was to get the adapter positions of the ViewHolders and then call the notifyItemMoved() publisher. When the RecyclerView receives that signal, it will automatically reorder the items for us. It is optional to update the original dataset for our tutorial App.

                override fun onMove(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                    target: RecyclerView.ViewHolder
                ): Boolean {
                    val fromPosition = viewHolder.adapterPosition
                    val toPosition = target.adapterPosition
    
                    //modifying the dataset as well is optional for this tutorial
     //                val movedItem = dataset.removeAt(fromPosition)
     //                dataset.add(toPosition, movedItem)
    
                    //push specific event
                    recyclerView.adapter?.notifyItemMoved(fromPosition, toPosition)
    
                    return true
                }
  3. Next, we will need to implement the other required member, onSwiped(). All we had to do was to remove the item at the requested position, and then call the notifyItemRemoved() publisher.

     override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        val position = viewHolder.adapterPosition
    
        //Actually removes the item from the dataset
        dataset.removeAt(position)
    
        //push specific event
        recyclerView.adapter?.notifyItemRemoved(position)
     }
Run the App

We only need two more lines of code before we can run the app.

  1. Create the ItemTouchHelper object.

     //Creates touch helper with callback
     val touchHelper = ItemTouchHelper(callback)
  2. Attach it to the RecyclerView.

     //attaches the helper to the recyclerView
     touchHelper.attachToRecyclerView(recyclerView)

Now, run the app. The swiping and dragging functions should work similarly to the Gif below.

SwipeDragRecycler.gif

Solution Code

MainActivity.kt

package com.codelab.daniwebrecyclerviewswipetoremove

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       //Creates a list of 10 elements
       val dataset = MutableList(10){
           Item("I am Item #$it")
       }

       //finds the RecyclerView
       val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)

       //Assigns an adapter to RecyclerView
       recyclerView.adapter = ItemAdapter(dataset)

       //Implementing the callback.
       val callback = object : ItemTouchHelper.SimpleCallback(
           ItemTouchHelper.UP or ItemTouchHelper.DOWN, //bitwise OR
           ItemTouchHelper.START or ItemTouchHelper.END //bitwise OR
       ) {
           override fun onMove(
               recyclerView: RecyclerView,
               viewHolder: RecyclerView.ViewHolder,
               target: RecyclerView.ViewHolder
           ): Boolean {
               val fromPosition = viewHolder.adapterPosition
               val toPosition = target.adapterPosition

               //modifying the dataset as well is optional for this tutorial
//                val movedItem = dataset.removeAt(fromPosition)
//                dataset.add(toPosition, movedItem)

               //push specific event
               recyclerView.adapter?.notifyItemMoved(fromPosition, toPosition)

               return true
           }

           override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
               val position = viewHolder.adapterPosition

               //Actually removes the item from the dataset
               dataset.removeAt(position)

               //push specific event
               recyclerView.adapter?.notifyItemRemoved(position)
           }

       }

       //Creates touch helper with callback
       val touchHelper = ItemTouchHelper(callback)

       //attaches the helper to the recyclerView
       touchHelper.attachToRecyclerView(recyclerView)
   }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/recycler_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
       app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

item_view.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/itemView"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="16dp"
   android:gravity="center"
   android:textSize="32sp"
   tools:text="TextView" />

Item.kt

package com.codelab.daniwebrecyclerviewswipetoremove

data class Item(
   val content: String
)

ItemAdapter.kt

package com.codelab.daniwebrecyclerviewswipetoremove

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class ItemAdapter(private val dataset: List<Item>): RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {

   class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
       val textView: TextView = itemView.findViewById(R.id.itemView)
   }

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
       val view = LayoutInflater.from(parent.context)
           .inflate(R.layout.item_view, parent, false)

       return ItemViewHolder(view)
   }

   override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
       holder.textView.text = dataset[position].content
   }

   override fun getItemCount() = dataset.size
}
Summary

We have learned how to add swipe-to-remove and drag-to-reorder in this tutorial. The full project code can be found at https://github.com/dmitrilc/DaniwebRecyclerViewSwipeToRemove.

GSAP Flip Plugin for Animation

Greensock made the GSAP Flip plugin free in the 3.9 release. FLIP is an animation concept that helps make super performance state-change animations. Ryan Mulligan has a good blog post:

FLIP, coined by Paul Lewis, is an acronym for First, Last, Invert, and Play. The Flip plugin harnesses this technique so that web developers can effortlessly and smoothly transition elements between states.

GSAP Flip plugin logo.

Examples using the GSAP Flip plugin

Taking advantage of FLIP “by hand” is certainly possible, but tricky. It’s an absolutely perfect thing for an animation library to do for us. Greenstock nailed it, as Ryan says:

1. Get the current state
2. Make your state changes
3. Call Flip.from(state, options)

Deliciously simple. Ryan made an “add to cart” effect with it:

I used it just the other day to make a “mini photo gallery” that could rotate which image was the big one on top:

Which, coincidently, is exactly why I ended up blogging “How to Cycle Through Classes on an HTML Element” the other day.

To Shared LinkPermalink on CSS-Tricks


GSAP Flip Plugin for Animation originally published on CSS-Tricks. You should get the newsletter.

Can WordPress Theme Changes Impact Google Ranking?

Can WordPress Theme Changes Impact Google Ranking?Your website’s theme lets search engines such as Google know what kind of content you’re offering your audience. The best WordPress themes also let your visitors easily navigate your layout and quickly find the type of content they’re looking for. From new colors and fonts to striking styles and layouts, a new website theme is […]

The post Can WordPress Theme Changes Impact Google Ranking? appeared first on WPExplorer.

Netlify Announces Beta Release of Netlify Graph

Netlify, a provider of a web developer platform that aims to improve developer productivity, has announced the release of Netlify Graph, a GraphQL-powered solution that aims to make building with APIs faster and easier. This release follows the company’s acquisition of OneGraph, a company that specializes in GraphQL resources. 

query mysql in fpdi file

I have the below file that opens a pdf and puts some text at the top. This is working fine. But what i want to do now is replace mypdf.pdf with the results of a mysql query. Similar to this:
$pageCount = $pdf->setSourceFile('../folder/folder/$mypdf');
and
$pdf->Write(0, '$sampletext');

Each person going to this page could receive a different pdf, hence the need to query the database to get the pdf they need.
Here is my code page:

<?php
//  brings the id from previous page to use in database query
$id = $_GET['id'];
// trying to query database errors the page


use setasign\Fpdi\Fpdi;
require_once 'fpdi/src/autoload.php';
require_once('fpdf/fpdf.php');

// initiate FPDI
$pdf = new Fpdi();

// get the page count
$pageCount = $pdf->setSourceFile('../folder/folder/mypdf.pdf');
// iterate through all pages
for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
    // import a page
    $templateId = $pdf->importPage($pageNo);

    $pdf->AddPage();
    // use the imported page and adjust the page size
    $pdf->useTemplate($templateId, ['adjustPageSize' => true]);

    // now write some text above the imported page
$pdf->SetFont('Helvetica');
$pdf->SetTextColor(255, 0, 0);
$pdf->SetXY(30, 10);
$pdf->Write(0, 'This is just a simple text');
}

// Output the new PDF
$pdf->Output();
?>

How can i query the database and put in my variables without erroring the page?

I hope this makes sense. If someone can point me in the right direction it would be appreciate.

Thanks.

6 Creative Ideas for CSS Link Hover Effects

Creating CSS link hover effects can add a bit of flair to an otherwise bland webpage. If you’ve ever found yourself stumped trying to make a slick hover effect, then I have six CSS effects for you to take and use for your next project.

A default link hover effect above a styled link hover effect with a rainbow underline.

Let’s get right to it!

I know we’re talking about :hover and all, but it can sometimes (but maybe not always) be a good idea lump :focus in as well, as not all interactions are directly from a mouse, but perhaps a tap or keystroke.

This effect applies a box shadow to the inline link, altering the color of the link text in the process. We start with padding all around the link, then add a negative margin of the same value to prevent the padding from disrupting the text flow.

We will use box-shadow instead of the background property since it allows us to transition.

a {
  box-shadow: inset 0 0 0 0 #54b3d6;
  color: #54b3d6;
  margin: 0 -.25rem;
  padding: 0 .25rem;
  transition: color .3s ease-in-out, box-shadow .3s ease-in-out;
}
a:hover {
  box-shadow: inset 100px 0 0 0 #54b3d6;
  color: white;
}

Here’s a fun one where we swap the text of the link with some other text on hover. Hover over the text and the linked text slides out as new text slides in.

Easier to show than tell.

There’s quite a bit of trickery happening in this link hover effect. But the magic sauce is using a data-attribute to define the text that slides in and call it with the content property of the link’s ::after pseudo-element.

First off, the HTML markup:

<p>Hover <a href="#" data-replace="get a link"><span>get a link</span></a></p>

That’s a lot of inline markup, but you’re looking at a paragraph tag that contains a link and a span.

Let’s give link some base styles. We need to give it relative positioning to hold the pseudo-elements — which will be absolutely positioned — in place, make sure it’s displayed as inline-block to get box element styling affordances, and hide any overflow the pseudo-elements might cause.

a {
  overflow: hidden;
  position: relative;
  display: inline-block;
}

The ::before and ::after pseudo-elements should have some absolute positioning so they stack with the actual link. We’ll make sure they are set to the link’s full width with a zero offset in the left position, setting them up for some sliding action.

a::before,
a::after {
 content: '';
  position: absolute;
  width: 100%;
  left: 0;
}

The ::after pseudo-element gets the content from the link’s data-attribute that’s in the HTML markup:

a::after {
  content: attr(data-replace);
}

Now we can transform: translate3d() the ::after pseudo-element element to the right by 200%. We move it back into position on :hover. While we’re at it, we can give this a zero offset n the top direction. This’ll be important later when we use the ::before pseudo-element like an underline below the text.

a::after {
  content: attr(data-replace);
  top: 0;
  transform-origin: 100% 50%;
  transform: translate3d(200%, 0, 0);
}

a:hover::after,
a:focus::after {
  transform: translate3d(0, 0, 0);
}

We’re also going to transform: scale() the ::before pseudo-element so it’s hidden by default, then scale it back up on :hover. We’ll make it small, like 2px in height, and pin it to the bottom so it looks like an underline on the text that swaps in with ::after.

a::before {
  background-color: #54b3d6;
  height: 2px;
  bottom: 0;
  transform-origin: 100% 50%;
  transform: scaleX(0);
}

a:hover::before,
a:focus::before {
  transform-origin: 0% 50%;
  transform: scaleX(1);
}

The rest is all preference! We drop in a transition on the transform effects, some colors, and whatnot to get the full effect. Those values are totally up to you.

View full CSS
a {
  overflow: hidden;
  position: relative;
  display: inline-block;
}

a::before,
a::after {
 content: '';
  position: absolute;
  width: 100%;
  left: 0;
}
a::before {
  background-color: #54b3d6;
  height: 2px;
  bottom: 0;
  transform-origin: 100% 50%;
  transform: scaleX(0);
  transition: transform .3s cubic-bezier(0.76, 0, 0.24, 1);
}
a::after {
  content: attr(data-replace);
  height: 100%;
  top: 0;
  transform-origin: 100% 50%;
  transform: translate3d(200%, 0, 0);
  transition: transform .3s cubic-bezier(0.76, 0, 0.24, 1);
  color: #54b3d6;
}

a:hover::before {
  transform-origin: 0% 50%;
  transform: scaleX(1);
}
a:hover::after {
  transform: translate3d(0, 0, 0);
}

a span {
  display: inline-block;
  transition: transform .3s cubic-bezier(0.76, 0, 0.24, 1);
}

a:hover span {
  transform: translate3d(-200%, 0, 0);
}

This is a pretty popular effect I’ve seen used in quite a few places. The idea is that you use the link’s ::before pseudo-element as a thick underline that sits slightly behind the actual text of the link. Then, on hover, the pseudo-element expands to cover the whole thing.

OK, some base styles for the link. We want no text-decoration since ::before will act like one, then some relative positioning to hold ::before in place when we give that absolute positioning.

a {
  text-decoration: none;
  position: relative;
}

Now let’s set up ::before by making it something like 8px tall so it looks like a thick underline. We’ll also give it absolute positioning so we have control to make it the full width of the actual link while offsetting it so it’s at the left and is just a smidge off the bottom so it looks like it’s subtly highlighting the link. May as well give it z-index: -1 so we’re assured it sits behind the link.

a::before {
  content: '';
  background-color: hsla(196, 61%, 58%, .75);
  position: absolute;
  left: 0;
  bottom: 3px;
  width: 100%;
  height: 8px;
  z-index: -1;
}

Nice, nice. Let’s make it appear as though ::before is growing when the link is hovered. All we need is to change the height from 3px to 100%. Notice that I’m also dropping the bottom offset back to zero so the background covers more space when it grows.

a:hover::before {
  bottom: 0;
  height: 100%;
}

Now for slight transition on those changes:

a::before {
  content: '';
  background-color: hsla(196, 61%, 58%, .75);
  position: absolute;
  left: 0;
  bottom: 3px;
  width: 100%;
  height: 8px;
  z-index: -1;
  transition: all .3s ease-in-out;
}
View full CSS
a {
  text-decoration: none;
  color: #18272F;
  font-weight: 700;
  position: relative;
}

a::before {
  content: '';
  background-color: hsla(196, 61%, 58%, .75);
  position: absolute;
  left: 0;
  bottom: 3px;
  width: 100%;
  height: 8px;
  z-index: -1;
  transition: all .3s ease-in-out;
}

a:hover::before {
  bottom: 0;
  height: 100%;
}

I personally like using this effect for links in a navigation. The link starts in one color without an underline. Then, on hover, a new color slides in from the right while an underline slides in from the left.

Neat, right? There’s a lot of motion happening in there, so you might consider the accessibility implications and wrap it all in a prefers-reduced-motion query to replace it with something more subtle for those with motion sensitivities.

Here’s how it works. We give the link a linear background gradient with a hard stop between two colors at the halfway mark.

a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
}

We make the background double the link’s width, or 200%, and position it all the way over to the left. That way, it’s like only one of the gradients two colors is showing.

a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
  background-size: 200% 100%;
  background-position: -100%;
}

The magic happens when we reach for a couple of non-standard -webkit-prefixed properties. One strips the color out of the text to make it transparent. The other clips the background gradient to the text so it appears the text is actually the color of the background.

a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
  background-size: 200% 100%;
  background-position: -100%;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

Still with me? Now let’s make the link’s faux underline by putting ::before to use. We’ll give it the same color we gave the on the hidden portion of the link’s background gradient and position it under the actual link so it looks like a proper text-decoration: underline.

a:before {
  content: '';
  background: #54b3d6;
  display: block;
  position: absolute;
  bottom: -3px;
  left: 0;
  width: 0;
  height: 3px;
}

On hover, we slide ::before into place, coming in from the left:

a:hover {
 background-position: 0;
}

Now, this is a little tricky. On hover, we make the link’s ::before pseudo-element 100% of the link’s width. If we were to apply this directly to the link’s hover, we’d make the link itself full-width, which moves it around the screen. Yikes!

a:hover::before {
  width: 100%;
}

Add a little transition to smooth things out:

a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
  background-size: 200% 100%;
  background-position: -100%;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  transition: all 0.3s ease-in-out;
}
View full CSS
a {
  background-image: linear-gradient(
    to right,
    #54b3d6,
    #54b3d6 50%,
    #000 50%
  );
  background-size: 200% 100%;
  background-position: -100%;
  display: inline-block;
  padding: 5px 0;
  position: relative;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  transition: all 0.3s ease-in-out;
}

a:before {
  content: '';
  background: #54b3d6;
  display: block;
  position: absolute;
  bottom: -3px;
  left: 0;
  width: 0;
  height: 3px;
  transition: all 0.3s ease-in-out;
}

a:hover {
 background-position: 0;
}

a:hover::before {
  width:100%;
}

We can’t do text-decoration-color: rainbow, but we can fake it with a little background magic mixed with linear gradients.

First, we remove the link’s text-decoration:

a {
  text-decoration: none;
}

Now for those gradients. We chain two linear gradients together on the same background property. One gradient is the initial color before hover. The second is the rainbow on hover.

a {
  background:
    linear-gradient(
      to right,
      rgba(100, 200, 200, 1),
      rgba(100, 200, 200, 1)
    ),
    linear-gradient(
      to right,
      rgba(255, 0, 0, 1),
      rgba(255, 0, 180, 1),
      rgba(0, 100, 200, 1)
  );
}

Let’s make the background size a mere 3px tall so it looks like, you know, an underline. We can size both gradients together on the background-size property so that the initial gradient is full width and 3px tall, and the rainbow is zero width.

a {
  background:
    linear-gradient(
      to right,
      rgba(100, 200, 200, 1),
      rgba(100, 200, 200, 1)
    ),
    linear-gradient(
      to right,
      rgba(255, 0, 0, 1),
      rgba(255, 0, 180, 1),
      rgba(0, 100, 200, 1)
  );
  background-size: 100% 3px, 0 3px;
}

Now we can position the background gradients — at the same time on the background-position property — so that the first gradient is fully in view and the rainbow is pushed out of view. Oh, and let’s make sure the background isn’t repeating while we’re at it.

a {
  background:
    linear-gradient(
      to right,
      rgba(100, 200, 200, 1),
      rgba(100, 200, 200, 1)
    ),
    linear-gradient(
      to right,
      rgba(255, 0, 0, 1),
      rgba(255, 0, 180, 1),
      rgba(0, 100, 200, 1)
  );
  background-size: 100% 3px, 0 3px;
  background-position: 100% 100%, 0 100%;
  background-repeat: no-repeat;
}

Let’s update the background-size on hover so that the gradients swap values:

a:hover {
  background-size: 0 3px, 100% 3px;
}

And, finally, a little transition when the hover takes place:

a {
  background:
    linear-gradient(
      to right,
      rgba(100, 200, 200, 1),
      rgba(100, 200, 200, 1)
    ),
    linear-gradient(
      to right,
      rgba(255, 0, 0, 1),
      rgba(255, 0, 180, 1),
      rgba(0, 100, 200, 1)
  );
  background-size: 100% 3px, 0 3px;
  background-position: 100% 100%, 0 100%;
  background-repeat: no-repeat;
  transition: background-size 400ms;
}

Voilà!

Geoff Graham actually covered this same one recently when he dissected Adam Argyle’s slick hover effect. In his demo, a background color enters from the left behind the link, then exits to the right on mouse out.

My version pares down the background so it’s more of an underline.

a {
  position: relative;
}

a::before {
    content: '';
    position: absolute;
    width: 100%;
    height: 4px;
    border-radius: 4px;
    background-color: #18272F;
    bottom: 0;
    left: 0;
    transform-origin: right;
    transform: scaleX(0);
    transition: transform .3s ease-in-out;
  }

a:hover::before {
  transform-origin: left;
  transform: scaleX(1);
}

That’s not the only way to accomplish this! Here’s another one by Justin Wong using background instead:

Geoff also has a roundup of CSS link hover effects, ranging from neat to downright absurd. Worth checking out!

Have a blast linking!

There are a lot of options when it comes to creating your own hover effect for in-line links with CSS. You can even play with these effects and create something new. I hope you liked the article. Keep experimenting!


6 Creative Ideas for CSS Link Hover Effects originally published on CSS-Tricks. You should get the newsletter.

WordPress.com Has a New Home on YouTube

(This is a sponsored post.)

✋ High fives to WordPress for releasing version 5.9 on January 29! This was the long-awaited introduction of the Site Editor and the reverberations are still being felt across the 43% slice of the web that is powered by WordPress.

The Site Editor is more than a neat feature: it’s a completely new approach to theming in WordPress. What makes it a big deal is that it lowers what was once a pretty high barrier to entry for anyone who wants to create or customize a WordPress theme, thanks to a visual interface that takes the PHP out of everything. If you’re interested more in this transition, check out Ganesh Dahal’s Deep Introduction to WordPress Block Themes.

Need a new template? All it takes is a click and dropping some blocks into place.

Learn the Site Editor on WordPress.com’s YouTube Page

The Site Editor, like many things about WordPress, is intuitive as heck. But it’s still such a new concept that it might be worth getting a few pointers on how to use it.

That’s why the WordPress.com team set up a brand spankin’ new YouTube channel full of fresh videos that walk you through it, including how full-site editing works, how to set up a homepage, and much more.

The idea is that this WordPress.com YouTube channel can be your go-to for all sorts of educational resources to support your ongoing website-building needs. There’s already a good amount of content in there with plans for more videos released regularly.

And just because the videos center around WordPress.com, anyone running a WordPress site, self-hosted or not, will benefit from these step-by-step tutorials.


WordPress.com Has a New Home on YouTube originally published on CSS-Tricks. You should get the newsletter.

How to Create a Facebook Ads Landing Page in WordPress

Do you want to create a landing page in WordPress for your Facebook ads?

The right landing page will motivate visitors from your Facebook ad campaign to take action, and convert them into leads and customers.

In this article, we’ll show you how to create a high-converting Facebook ads landing page in WordPress.

How to Create a Facebook Ads Landing Page in WordPress

Why Create a Facebook Ads Landing Page in WordPress?

Are you running Facebook ads? When someone on Facebook clicks on one of your ads, you want to direct them to your WordPress website.

But if they land on your homepage, they may lose interest or not be able to find what they’re looking for. That’s why you want to link to a dedicated landing page instead.

A landing page, also known as a squeeze page, is designed for a specific purpose and high conversion rates. It’s where those who responded to your ad can learn more about what you’re promoting and take the next step.

An effective landing page will match the design, tone, and content of your ad. It will convey a sense of urgency that motivates the user to act now.

You might use images and video to explain detailed information quickly, and social proof such as testimonials and reviews to demonstrate why other people love your business.

With that being said, let’s look at how to create a Facebook ads landing page in WordPress.

How to Create a Facebook Ads Landing Page in WordPress

First, you need to install and activate the SeedProd plugin. For more details, see our step by step guide on how to install a WordPress plugin.

SeedProd is the best landing page plugin for WordPress and allows you to create any type of landing page quickly and easily without any coding or design skills. While there’s a free version of SeedProd, we’ll be using the Pro version since it offers more powerful features.

Upon activation, you’ll see a welcome screen where you can enter your license key. You can find this information under your account on the SeedProd website. After pasting your license key, you’ll need to click the ‘Verify key’ button.

Enter Your SeedProd License Key

Next, you should scroll down until you see the ‘Create Your First Page’ button.

Simply click the button to get started.

Click 'Create Your First Page'

This will take you to SeedProd’s landing page dashboard where you can see landing page modes for different types of pages.

You need to click the button labeled ‘+ Add New Landing Page’ to get started.

Click 'Add New Landing Page'

Next, you need to choose a template as a starting point for your page. SeedProd comes with dozens of beautiful landing page templates neatly organized for different campaign types.

You can filter the templates by clicking one of the tabs along the top, such as ‘Lead Squeeze’. When you hover your mouse over a template, you can preview it by clicking the magnifying glass icon, or select it by clicking the red tick icon.

Select a Template by Clicking the Tick Icon

After selecting a template, you’ll be asked to give your new page a name.

The page URL will be filled in automatically, but you can change it if you wish. You will need to add this URL to your Facebook ad later on, so make a note of it.

Enter Your New Page Details

Once you click the button labeled ‘Save and Start Editing the Page’, you’ll be taken to the SeedProd drag and drop page builder.

You’ll see blocks on the left that let you add content to your landing page, such as headlines, lists, images, or buttons, and a preview of how your page will look on the right.

SeedProd's Drag and Drop Page Builder

To create an effective landing page, you’ll need to customize it to match your Facebook ad. You can change anything in the preview pane simply by clicking on it, and add additional blocks using drag and drop.

First, you need to click the headline so that you can see its contents on the left of the screen, where you can edit it to match your ad. Simply delete the existing text and type in your own.

Edit the Headline

You’ll see a formatting toolbar just above the text box, and underneath there are icons for text alignment, a slider to adjust the font size, and more. You’ll find more formatting options in the Advanced tab, such as typography, text color and shadow, spacing, and device visibility.

Next, we’ll add a different background image to the top of the page. To do that, move your mouse to the top of the preview pane until you see a purple toolbar. After that, you need to click the cog icon to display settings for that section.

Click the Settings Icon on the Purple Toolbar

Here you can change the background image to match your Facebook ad. You need to click the red background image icon to open your WordPress media library. Here you can select or upload the background image from your ad.

You can also customize the optin subscription box that came included in the template. To do that, you need to hover your mouse over the email address and then click the cog icon to display the settings for that block.

Customize the Optin Form

You can now customize the optin fields and the size and alignment of the box. You can also change the wording, size, and color of the ‘Submit’ button and customize the success action.

If your promotion is a limited time offer, then it’s smart to add a sense of urgency with a countdown timer. To do that, simply locate the ‘Countdown’ block and drag it onto your page.

Add a Countdown Timer Block

Next, you need to set the expiration date and time.

Once you click the cog icon to access the block’s settings, you will be able to pick the date and time that the offer ends. Don’t forget to choose the correct time zone.

Configure the Countdown Timer Block

Once you’re happy that your landing page matches the Facebook ad and contains all of the necessary information, it’s time to publish it.

To do that, you should click the drop down menu button next to Save in the top right corner and then click ‘Publish’.

When Finished Publish Your Landing Page

How to Link Your Facebook Ad to Your Landing Page

Now that your landing page is published, you can add the link to your Facebook ad.

You should head over to your Facebook page where you can create or edit your ad. To create a new ad you need to navigate to ‘Ad Center’ and click on the ‘Create Ad’ button. Or you can access your existing ads by clicking on ‘All ads’.

Open the Facebook Ad Center

At the top of your ad, you will notice a section called ‘Goal’.

This should say ‘Get more website visitors‘. If it doesn’t, then you need to click the ‘Change’ button to adjust it.

Select the Goal Get More Website Visitors

You should then select ‘Get more website visitors’ from the list.

Then, click on the ‘Save’ button to store your choice.

Select the Goal Get More Website Visitors

After that, you need to scroll down to the Website URL field and enter the landing page’s URL that you made note of earlier.

Enter the URL to Your Landing Page

Once you are happy with your ad and have selected a payment method, you should click on the ‘Promote now’ button at the bottom of the page to push your Facebook ad live.

Click the Promote Now Button

That’s it! Next, you may want to set up tracking in your website so you can measure the effectiveness of your Facebook ad landing page. You can learn how by following our step by step guide on WordPress conversion tracking made simple.

We hope this tutorial helped you learn how to create a landing page for Facebook ads in WordPress. You may also want to learn how to create an email newsletter the right way, or check out our list of proven ways to make money online blogging with WordPress.

If you liked this article, then please subscribe to our YouTube Channel for WordPress video tutorials. You can also find us on Twitter and Facebook.

The post How to Create a Facebook Ads Landing Page in WordPress first appeared on WPBeginner.

10 Best Books and Courses To Learn Spring Framework in Depth

Hello Java developers, if you want to learn Spring Framework and looking for the best resources like books, online courses, code katas, and tutorials then you have come to the right place.

Earlier, I have shared the best Spring Boot courses and today, I Am going to share the best Spring Framework resources for Java developers. This includes learning Spring, course, code katas, and interactive study material.